using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using IppServer.Host.Configuration;
using IppServer.Host.Printing;

namespace IppServer.Host.WebUI
{
    internal class AdminWebUI
    {
        private const int CompletedJobVisibleSeconds = 10;
        private const int CompletedJobRetentionSeconds = 20;

        private ServerConfig _config; // 改为可变，支持热更新
        private readonly object _configLock = new object(); // 配置更新锁
        private readonly HttpListener _listener;
        private readonly Func<List<string>> _getLocalIpAddresses;
        private readonly Func<string, int> _extractPortFromPrefix;
        private readonly Func<string, string> _extractPathFromPrefix;
        private readonly ConcurrentDictionary<string, List<IppServerHost.CompletedJob>> _completedJobs;
        private readonly bool _enableMdnsDiscovery;
        private readonly Action<string> _logCallback;
        private readonly Action _reloadConfigCallback; // 配置重载回调
        private readonly Func<int, Task<(bool Success, string Message)>> _stopJobCallback;
        private readonly Func<int, Task<(bool Success, string Message)>> _replayJobCallback;
        private readonly Func<int, Task<(bool Success, string Message)>> _deleteJobCallback;
        private readonly Func<List<IppServerHost.LogEntry>> _getLogsCallback; // 获取日志回调
        private readonly Action _clearLogsCallback; // 清除日志回调
        private readonly Func<string> _exportLogsCallback; // 导出日志回调
        private Func<bool> _getIppServiceStateCallback;
        private Func<(bool Success, string Message)> _startIppServiceCallback;
        private Func<(bool Success, string Message)> _stopIppServiceCallback;
        private Func<(bool Success, string Message)> _restartIppServiceCallback;
        // TCP 9100 服务回调
        private Func<bool> _getTcpServiceStateCallback;
        private Func<(bool Success, string Message)> _startTcpServiceCallback;
        private Func<(bool Success, string Message)> _stopTcpServiceCallback;
        private Func<List<Configuration.TcpPrinterTask>> _getTcpTasksCallback;
        private Func<Configuration.TcpPrinterTask, (bool Success, string Message)> _addTcpTaskCallback;
        private Func<int, (bool Success, string Message)> _removeTcpTaskCallback;
        private Func<int, Configuration.TcpPrinterTask, (bool Success, string Message)> _updateTcpTaskCallback;
        private Func<List<IppServerHost.TcpJob>> _getTcpJobsCallback;
        // 认证管理器
        private readonly AuthManager _authManager = new AuthManager();

        public AdminWebUI(
            ServerConfig config,
            HttpListener listener,
            Func<List<string>> getLocalIpAddresses,
            Func<string, int> extractPortFromPrefix,
            Func<string, string> extractPathFromPrefix,
            ConcurrentDictionary<string, List<IppServerHost.CompletedJob>> completedJobs,
            bool enableMdnsDiscovery,
            Action<string> logCallback,
            Action reloadConfigCallback,
            Func<int, Task<(bool Success, string Message)>> stopJobCallback,
            Func<int, Task<(bool Success, string Message)>> replayJobCallback,
            Func<int, Task<(bool Success, string Message)>> deleteJobCallback,
            Func<List<IppServerHost.LogEntry>> getLogsCallback,
            Action clearLogsCallback,
            Func<string> exportLogsCallback)
        {
            _config = config;
            _listener = listener;
            _getLocalIpAddresses = getLocalIpAddresses;
            _extractPortFromPrefix = extractPortFromPrefix;
            _extractPathFromPrefix = extractPathFromPrefix;
            _completedJobs = completedJobs;
            _enableMdnsDiscovery = enableMdnsDiscovery;
            _logCallback = logCallback;
            _reloadConfigCallback = reloadConfigCallback;
            _stopJobCallback = stopJobCallback;
            _replayJobCallback = replayJobCallback;
            _deleteJobCallback = deleteJobCallback;
            _getLogsCallback = getLogsCallback;
            _clearLogsCallback = clearLogsCallback;
            _exportLogsCallback = exportLogsCallback;
        }
        
        // 带 TCP 服务回调的构造函数重载
        public AdminWebUI(
            ServerConfig config,
            HttpListener listener,
            Func<List<string>> getLocalIpAddresses,
            Func<string, int> extractPortFromPrefix,
            Func<string, string> extractPathFromPrefix,
            ConcurrentDictionary<string, List<IppServerHost.CompletedJob>> completedJobs,
            bool enableMdnsDiscovery,
            Action<string> logCallback,
            Action reloadConfigCallback,
            Func<int, Task<(bool Success, string Message)>> stopJobCallback,
            Func<int, Task<(bool Success, string Message)>> replayJobCallback,
            Func<int, Task<(bool Success, string Message)>> deleteJobCallback,
            Func<List<IppServerHost.LogEntry>> getLogsCallback,
            Action clearLogsCallback,
            Func<string> exportLogsCallback,
            Func<bool> getIppServiceStateCallback,
            Func<(bool Success, string Message)> startIppServiceCallback,
            Func<(bool Success, string Message)> stopIppServiceCallback,
            Func<(bool Success, string Message)> restartIppServiceCallback,
            Func<bool> getTcpServiceStateCallback,
            Func<(bool Success, string Message)> startTcpServiceCallback,
            Func<(bool Success, string Message)> stopTcpServiceCallback,
            Func<List<Configuration.TcpPrinterTask>> getTcpTasksCallback,
            Func<Configuration.TcpPrinterTask, (bool Success, string Message)> addTcpTaskCallback,
            Func<int, (bool Success, string Message)> removeTcpTaskCallback,
            Func<int, Configuration.TcpPrinterTask, (bool Success, string Message)> updateTcpTaskCallback,
            Func<List<IppServerHost.TcpJob>> getTcpJobsCallback)
            : this(
                config,
                listener,
                getLocalIpAddresses,
                extractPortFromPrefix,
                extractPathFromPrefix,
                completedJobs,
                enableMdnsDiscovery,
                logCallback,
                reloadConfigCallback,
                stopJobCallback,
                replayJobCallback,
                deleteJobCallback,
                getLogsCallback,
                clearLogsCallback,
                exportLogsCallback)
        {
            ConfigureServiceControlCallbacks(
                getIppServiceStateCallback,
                startIppServiceCallback,
                stopIppServiceCallback,
                restartIppServiceCallback);
            ConfigureTcpServiceCallbacks(
                getTcpServiceStateCallback,
                startTcpServiceCallback,
                stopTcpServiceCallback,
                getTcpTasksCallback,
                addTcpTaskCallback,
                removeTcpTaskCallback,
                updateTcpTaskCallback,
                getTcpJobsCallback);
        }

        public void ConfigureServiceControlCallbacks(
            Func<bool> getIppServiceStateCallback,
            Func<(bool Success, string Message)> startIppServiceCallback,
            Func<(bool Success, string Message)> stopIppServiceCallback,
            Func<(bool Success, string Message)> restartIppServiceCallback)
        {
            _getIppServiceStateCallback = getIppServiceStateCallback;
            _startIppServiceCallback = startIppServiceCallback;
            _stopIppServiceCallback = stopIppServiceCallback;
            _restartIppServiceCallback = restartIppServiceCallback;
        }
        
        public void ConfigureTcpServiceCallbacks(
            Func<bool> getTcpServiceStateCallback,
            Func<(bool Success, string Message)> startTcpServiceCallback,
            Func<(bool Success, string Message)> stopTcpServiceCallback,
            Func<List<Configuration.TcpPrinterTask>> getTcpTasksCallback,
            Func<Configuration.TcpPrinterTask, (bool Success, string Message)> addTcpTaskCallback,
            Func<int, (bool Success, string Message)> removeTcpTaskCallback,
            Func<int, Configuration.TcpPrinterTask, (bool Success, string Message)> updateTcpTaskCallback,
            Func<List<IppServerHost.TcpJob>> getTcpJobsCallback)
        {
            _getTcpServiceStateCallback = getTcpServiceStateCallback;
            _startTcpServiceCallback = startTcpServiceCallback;
            _stopTcpServiceCallback = stopTcpServiceCallback;
            _getTcpTasksCallback = getTcpTasksCallback;
            _addTcpTaskCallback = addTcpTaskCallback;
            _removeTcpTaskCallback = removeTcpTaskCallback;
            _updateTcpTaskCallback = updateTcpTaskCallback;
            _getTcpJobsCallback = getTcpJobsCallback;
        }
        
        /// <summary>
        /// 更新配置引用（由IppServerHost调用）
        /// </summary>
        public void UpdateConfig(ServerConfig newConfig)
        {
            lock (_configLock)
            {
                _config = newConfig;
            }
        }

        public async Task WriteAdminPageAsync(HttpListenerRequest request, HttpListenerResponse response)
        {
            // 检查认证状态
            var cookieHeader = request.Headers["Cookie"];
            var sessionId = _authManager.GetSessionIdFromCookie(cookieHeader);
            
            if (!_authManager.ValidateSession(sessionId))
            {
                // 未认证，返回登录页面
                await WriteLoginPageAsync(response).ConfigureAwait(false);
                return;
            }
            
            // 已认证，返回管理页面
            var html = BuildAdminHtml();
            var buffer = Encoding.UTF8.GetBytes(html);
            response.StatusCode = (int)HttpStatusCode.OK;
            response.ContentType = "text/html; charset=utf-8";
            response.ContentLength64 = buffer.Length;
            await response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
        }

        /// <summary>
        /// 写入登录页面
        /// </summary>
        private async Task WriteLoginPageAsync(HttpListenerResponse response)
        {
            var html = BuildLoginHtml();
            var buffer = Encoding.UTF8.GetBytes(html);
            response.StatusCode = (int)HttpStatusCode.OK;
            response.ContentType = "text/html; charset=utf-8";
            response.ContentLength64 = buffer.Length;
            await response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
        }

        /// <summary>
        /// 构建登录页面HTML
        /// </summary>
        private string BuildLoginHtml()
        {
            return @"<!DOCTYPE html>
<html lang=""zh-CN"">
<head>
    <meta charset=""utf-8"">
    <meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
    <title>IPP Server - 管理员登录</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Microsoft YaHei', 'Segoe UI', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }
        .login-container {
            background: white;
            border-radius: 10px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.2);
            padding: 40px;
            width: 100%;
            max-width: 400px;
        }
        .login-header {
            text-align: center;
            margin-bottom: 30px;
        }
        .login-header h1 {
            color: #333;
            font-size: 24px;
            margin-bottom: 10px;
        }
        .login-header p {
            color: #666;
            font-size: 14px;
        }
        .form-group {
            margin-bottom: 20px;
        }
        .form-group label {
            display: block;
            color: #333;
            margin-bottom: 8px;
            font-size: 14px;
            font-weight: 500;
        }
        .form-group input {
            width: 100%;
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            transition: border-color 0.3s;
        }
        .form-group input:focus {
            outline: none;
            border-color: #667eea;
        }
        .btn-login {
            width: 100%;
            padding: 12px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            font-weight: 500;
            cursor: pointer;
            transition: opacity 0.3s;
        }
        .btn-login:hover {
            opacity: 0.9;
        }
        .btn-login:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }
        .error-message {
            background: #fee;
            color: #c33;
            padding: 10px;
            border-radius: 5px;
            margin-bottom: 20px;
            font-size: 14px;
            display: none;
        }
        .error-message.show {
            display: block;
        }
        .info-message {
            background: #e3f2fd;
            color: #1976d2;
            padding: 10px;
            border-radius: 5px;
            margin-bottom: 20px;
            font-size: 13px;
            line-height: 1.5;
        }
    </style>
</head>
<body>
    <div class=""login-container"">
        <div class=""login-header"">
            <h1>🔐 管理员登录</h1>
            <p>IPP Server 管理控制台</p>
        </div>
        <div class=""error-message"" id=""errorMessage""></div>
        <div class=""info-message"" id=""infoMessage"">
            <strong>提示：</strong>首次登录时，如果未设置密码，可以输入任意密码进行登录并设置新密码。
        </div>
        <form id=""loginForm"">
            <div class=""form-group"">
                <label for=""password"">管理员密码</label>
                <input type=""password"" id=""password"" name=""password"" required autofocus placeholder=""请输入管理员密码"">
            </div>
            <button type=""submit"" class=""btn-login"" id=""loginBtn"">登录</button>
        </form>
    </div>
    <script>
        var apiBase = window.location.pathname.replace(/\/admin.*$/, '') + '/api/auth';
        document.getElementById('loginForm').addEventListener('submit', async function(e) {
            e.preventDefault();
            var password = document.getElementById('password').value;
            var loginBtn = document.getElementById('loginBtn');
            var errorMsg = document.getElementById('errorMessage');
            
            if (!password) {
                errorMsg.textContent = '请输入密码';
                errorMsg.classList.add('show');
                return;
            }
            
            loginBtn.disabled = true;
            loginBtn.textContent = '登录中...';
            errorMsg.classList.remove('show');
            
            try {
                var response = await fetch(apiBase + '/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ password: password })
                });
                
                var data = await response.json();
                
                if (response.ok && data.success) {
                    // 登录成功，跳转到管理页面
                    window.location.href = window.location.pathname.replace(/\/login.*$/, '/admin') || '/admin';
                } else {
                    errorMsg.textContent = data.message || '登录失败，请检查密码';
                    errorMsg.classList.add('show');
                    loginBtn.disabled = false;
                    loginBtn.textContent = '登录';
                }
            } catch (error) {
                errorMsg.textContent = '登录失败：' + error.message;
                errorMsg.classList.add('show');
                loginBtn.disabled = false;
                loginBtn.textContent = '登录';
            }
        });
    </script>
</body>
</html>";
        }

        private string BuildAdminHtml()
        {
            var sb = new StringBuilder();
            sb.Append("<!DOCTYPE html><html lang=\"zh-cn\"><head><meta charset=\"utf-8\"/>");
            sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>");
            sb.Append("<title>IPP Server 管理界面</title>");
            sb.Append(BuildAdminCss());
            sb.Append("</head><body>");
            sb.Append(BuildAdminBody());
            sb.Append(BuildAdminJs());
            sb.Append("</body></html>");
            return sb.ToString();
        }

        private string BuildAdminCss()
        {
            return @"
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif; background: #f5f7fa; color: #333; }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px 30px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header h1 { font-size: 24px; font-weight: 600; }
.btn-logout { background: rgba(255,255,255,0.2); color: white; border: 1px solid rgba(255,255,255,0.3); padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: all 0.3s; }
.btn-logout:hover { background: rgba(255,255,255,0.3); border-color: rgba(255,255,255,0.5); }
.btn-logout:active { background: rgba(255,255,255,0.4); }
.container { display: flex; min-height: calc(100vh - 70px); }
.sidebar { width: 250px; background: white; box-shadow: 2px 0 10px rgba(0,0,0,0.05); padding: 20px 0; }
.sidebar-item { padding: 15px 30px; cursor: pointer; transition: all 0.3s; border-left: 3px solid transparent; }
.sidebar-item:hover { background: #f8f9fa; border-left-color: #667eea; }
.sidebar-item.active { background: #f0f4ff; border-left-color: #667eea; color: #667eea; font-weight: 600; }
.sidebar-item i { margin-right: 10px; width: 20px; display: inline-block; }
.content { flex: 1; padding: 30px; overflow-y: auto; }
.content-section { display: none; animation: fadeIn 0.3s; }
.content-section.active { display: block; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.card { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
.card h2 { font-size: 18px; margin-bottom: 15px; color: #333; border-bottom: 2px solid #f0f0f0; padding-bottom: 10px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 20px; }
.stat-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 8px; border: none; width: 100%; text-align: left; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; display: block; }
.stat-card:focus { outline: none; }
.stat-card:hover { transform: translateY(-3px); box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3); }
.stat-card h3 { font-size: 14px; opacity: 0.9; margin-bottom: 10px; }
.stat-card .value { font-size: 32px; font-weight: 600; }
.queue-table { width: 100%; border-collapse: collapse; }
.queue-table th, .queue-table td { padding: 12px; text-align: left; border-bottom: 1px solid #f0f0f0; }
.queue-table th { background: #f8f9fa; font-weight: 600; color: #555; }
.queue-table tr:hover { background: #f8f9fa; }
.queue-table tr.pending { background: #fff3cd; }
.queue-table tr.printing { background: #d1ecf1; }
.queue-table tr.completed { background: #d4edda; }
.queue-table tr.error { background: #f8d7da; }
.jobs-tabs { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 15px; }
.job-tab { border: 1px solid #e0e7ff; background: #fff; color: #4f46e5; border-radius: 999px; padding: 6px 14px; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s; }
.job-tab .count { background: #eef2ff; border-radius: 999px; padding: 2px 8px; font-weight: 600; font-size: 12px; }
.job-tab.active { background: #4f46e5; color: #fff; box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2); }
.job-tab.active .count { background: rgba(255,255,255,0.25); color: #fff; }
.jobs-table-wrapper { overflow-x: auto; }
.jobs-table { width: 100%; border-collapse: collapse; }
.jobs-table th, .jobs-table td { padding: 10px 12px; border-bottom: 1px solid #f0f0f0; text-align: left; font-size: 13px; white-space: nowrap; }
.jobs-table th { background: #f9fafb; text-transform: uppercase; font-size: 11px; letter-spacing: 0.05em; color: #6b7280; }
.jobs-table td strong { color: #111827; }
.jobs-table tbody tr:hover { background: #f9fafb; }
.job-status-badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; font-weight: 600; font-size: 12px; }
.job-status-badge .dot { width: 8px; height: 8px; border-radius: 50%; }
.job-status-badge.printing { background: #dbeafe; color: #1d4ed8; }
.job-status-badge.printing .dot { background: #1d4ed8; }
.job-status-badge.completed { background: #dcfce7; color: #15803d; }
.job-status-badge.completed .dot { background: #15803d; }
.job-status-badge.error { background: #fee2e2; color: #b91c1c; }
.job-status-badge.error .dot { background: #b91c1c; }
.job-status-badge.stopped { background: #ede9fe; color: #5b21b6; }
.job-status-badge.stopped .dot { background: #5b21b6; }
.job-status-badge.pending { background: #fff7ed; color: #c2410c; }
.job-status-badge.pending .dot { background: #c2410c; }
.job-actions { display: flex; gap: 6px; }
.job-actions button { border: none; border-radius: 6px; padding: 4px 10px; font-size: 12px; cursor: pointer; transition: opacity 0.2s; }
.job-actions button:disabled { opacity: 0.5; cursor: not-allowed; }
.action-stop { background: #fee2e2; color: #b91c1c; }
.action-reprint { background: #dcfce7; color: #15803d; }
.action-delete { background: #e5e7eb; color: #374151; }
.jobs-empty { text-align: center; padding: 40px; color: #9ca3af; font-size: 14px; }
.status-badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500; }
.status-pending { background: #ffc107; color: #856404; }
.status-printing { background: #17a2b8; color: #fff; }
.status-completed { background: #28a745; color: #fff; }
.status-error { background: #dc3545; color: #fff; }
.status-stopped { background: #6c757d; color: #fff; }
.btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: all 0.3s; }
.btn-primary { background: #667eea; color: white; }
.btn-primary:hover { background: #5568d3; }
.btn-danger { background: #dc3545; color: white; }
.btn-danger:hover { background: #c82333; }
.btn-success { background: #28a745; color: white; }
.btn-success:hover { background: #218838; }
.btn-warning { background: #ffc107; color: #333; }
.btn-warning:hover { background: #e0a800; }
.btn-sm { padding: 4px 8px; font-size: 12px; margin: 0 2px; }
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
.btn:disabled:hover { background: inherit; }
.system-tabs { margin-top: 20px; }
.tab-headers { display: flex; border-bottom: 2px solid #e5e7eb; margin-bottom: 20px; }
.tab-header { padding: 12px 24px; background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; font-size: 14px; color: #6b7280; transition: all 0.3s; margin-bottom: -2px; }
.tab-header:hover { color: #667eea; }
.tab-header.active { color: #667eea; border-bottom-color: #667eea; font-weight: 600; }
.tab-content { position: relative; }
.tab-pane { display: none; animation: fadeIn 0.3s; }
.tab-pane.active { display: block; }
.form-group { margin-bottom: 20px; }
.form-group label { display: block; margin-bottom: 8px; font-weight: 500; color: #374151; }
.form-group input, .form-group select { width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 4px; font-size: 14px; }
.form-group input:focus, .form-group select:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); }
.form-group .help-text { margin-top: 4px; font-size: 12px; color: #6b7280; }
.checkbox-group { display: flex; align-items: center; margin-bottom: 12px; }
.checkbox-group input[type=""checkbox""] { width: auto; margin-right: 8px; }
.log-container { background: #1e293b; color: #e2e8f0; padding: 15px; border-radius: 4px; font-family: 'Consolas', 'Monaco', monospace; font-size: 12px; max-height: 500px; overflow-y: auto; white-space: pre-wrap; word-wrap: break-word; }
.log-container .log-entry { margin-bottom: 4px; }
.log-container .log-entry.error { color: #f87171; }
.log-container .log-entry.warning { color: #fbbf24; }
.log-container .log-entry.info { color: #60a5fa; }
.printer-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 20px; }
.printer-card { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); transition: all 0.3s; border: 2px solid transparent; min-width: 400px; }
.printer-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); border-color: #667eea; }
.printer-card.active { border-color: #667eea; background: #f0f4ff; }
.printer-card-header { cursor: pointer; }
.printer-card-header:hover { opacity: 0.8; }
.printer-card-actions { margin-top: 10px; }
.printer-card h3 { font-size: 16px; margin-bottom: 10px; color: #333; }
.printer-card .printer-info { color: #666; font-size: 14px; margin: 5px 0; }
.printer-card .printer-uri { background: #f8f9fa; padding: 8px; border-radius: 4px; font-family: monospace; font-size: 12px; margin-top: 10px; word-break: break-all; display: flex; align-items: center; gap: 8px; }
.printer-card .printer-uri code { flex: 1; word-break: break-all; }
.copy-btn { background: #667eea; color: white; border: none; padding: 4px 12px; border-radius: 4px; cursor: pointer; font-size: 11px; white-space: nowrap; flex-shrink: 0; }
.copy-btn:hover { background: #5568d3; }
.copy-btn:disabled { background: #ccc; cursor: not-allowed; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; color: #555; }
.form-group input, .form-group select { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.form-group input:focus, .form-group select:focus { outline: none; border-color: #667eea; }
.btn-group { display: flex; gap: 10px; margin-top: 20px; }
.empty-state { text-align: center; padding: 40px; color: #999; }
.empty-state i { font-size: 48px; margin-bottom: 10px; opacity: 0.5; }
.loading { text-align: center; padding: 40px; }
.spinner { border: 3px solid #f3f3f3; border-top: 3px solid #667eea; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>";
        }

        private string BuildAdminBody()
        {
            // 线程安全地获取当前配置
            ServerConfig currentConfig;
            lock (_configLock)
            {
                currentConfig = _config;
            }
            
            var localIps = _getLocalIpAddresses();
            var port = _extractPortFromPrefix(currentConfig.BasePrefix);
            var baseUrl = localIps.Count > 0 ? $"http://{localIps[0]}:{port}" : currentConfig.BasePrefix.TrimEnd('/');
            
            return $@"
<div class=""header"">
    <div style=""display: flex; justify-content: space-between; align-items: center;"">
        <h1 style=""margin: 0;"">🖨️ IPP Server 管理界面</h1>
        <button id=""btnLogout"" class=""btn-logout"" onclick=""logout()"" title=""退出登录"">🚪 退出登录</button>
    </div>
</div>
<div class=""container"">
    <div class=""sidebar"">
        <div class=""sidebar-item active"" data-section=""overview"">
            <i>📊</i>概览
        </div>
        <div class=""sidebar-item"" data-section=""jobs"">
            <i>📋</i>打印队列
        </div>
        <div class=""sidebar-item"" data-section=""printers"">
            <i>🖨️</i>打印机管理
        </div>
        <div class=""sidebar-item"" data-section=""tcp"">
            <i>🔌</i>TCP/IP 打印服务
        </div>
        <div class=""sidebar-item"" data-section=""system"">
            <i>⚙️</i>系统管理
        </div>
        <div class=""sidebar-item"" data-section=""about"">
            <i>ℹ️</i>关于系统
        </div>
    </div>
    <div class=""content"">
        <div class=""content-section active"" id=""overview"">
            {BuildOverviewSection(baseUrl, port)}
        </div>
        <div class=""content-section"" id=""jobs"">
            {BuildJobsSection()}
        </div>
        <div class=""content-section"" id=""printers"">
            {BuildPrintersSection(baseUrl, port)}
        </div>
        <div class=""content-section"" id=""tcp"">
            {BuildTcpSection()}
        </div>
        <div class=""content-section"" id=""system"">
            {BuildSystemSection()}
        </div>
        <div class=""content-section"" id=""about"">
            {BuildAboutSection()}
        </div>
    </div>
</div>";
        }

        private string BuildOverviewSection(string baseUrl, int port)
        {
            // 线程安全地获取当前配置
            ServerConfig currentConfig;
            lock (_configLock)
            {
                currentConfig = _config;
            }
            
            var queueCount = currentConfig.QueueToPrinter.Count;
            var installedCount = GetPhysicalPrinters().Count;
            var isRunning = _listener.IsListening;
            var localIps = _getLocalIpAddresses();
            
            var sb = new StringBuilder();
            sb.Append("<div class=\"stats-grid\">");
            sb.Append($"<button class=\"stat-card\" data-target=\"system\" type=\"button\" aria-label=\"查看系统状态\"><h3>服务状态</h3><div class=\"value\">{(isRunning ? "运行中" : "已停止")}</div></button>");
            sb.Append($"<button class=\"stat-card\" data-target=\"printers\" type=\"button\" aria-label=\"查看打印机管理\"><h3>物理打印机</h3><div class=\"value\">{installedCount}</div></button>");
            sb.Append($"<button class=\"stat-card\" data-target=\"printers\" type=\"button\" aria-label=\"查看共享队列\"><h3>共享队列</h3><div class=\"value\">{queueCount}</div></button>");
            sb.Append("</div>");
            
            sb.Append("<div class=\"card\"><h2>共享队列列表</h2>");
            if (queueCount == 0)
            {
                sb.Append("<div class=\"empty-state\"><i>📭</i><p>当前没有共享队列</p></div>");
            }
            else
            {
                sb.Append("<table class=\"queue-table\"><thead><tr><th>队列名称</th><th>目标打印机</th><th>URI地址</th><th>操作</th></tr></thead><tbody>");
                foreach (var kvp in currentConfig.QueueToPrinter)
                {
                    var queueName = WebUtility.HtmlEncode(kvp.Key);
                    var printerName = WebUtility.HtmlEncode(kvp.Value);
                    var uri = $"{baseUrl}/printers/{kvp.Key}";
                    var safeUri = WebUtility.HtmlEncode(uri);
                    sb.Append($"<tr><td><strong>{queueName}</strong></td><td>{printerName}</td><td><code>{safeUri}</code><button class=\"copy-btn\" data-action=\"copy-uri\" data-uri=\"{safeUri}\">复制</button></td><td><button class=\"copy-btn\" data-action=\"test-print\" data-uri=\"{safeUri}\">测试打印</button></td></tr>");
                }
                sb.Append("</tbody></table>");
            }
            sb.Append("</div>");
            
            return sb.ToString();
        }

        private string BuildJobsSection()
        {
            return @"
<div class=""card"">
    <h2>打印队列</h2>
    <div class=""jobs-tabs"">
        <button class=""job-tab active"" data-job-tab=""all"">全部作业<span class=""count"" id=""jobCountAll"">0</span></button>
        <button class=""job-tab"" data-job-tab=""pending"">等待中<span class=""count"" id=""jobCountPending"">0</span></button>
        <button class=""job-tab"" data-job-tab=""printing"">打印中<span class=""count"" id=""jobCountPrinting"">0</span></button>
        <button class=""job-tab"" data-job-tab=""completed"">已完成<span class=""count"" id=""jobCountCompleted"">0</span></button>
        <button class=""job-tab"" data-job-tab=""stopped"">已停止<span class=""count"" id=""jobCountStopped"">0</span></button>
        <button class=""job-tab"" data-job-tab=""error"">错误<span class=""count"" id=""jobCountError"">0</span></button>
    </div>
    <div class=""jobs-table-wrapper"">
        <table class=""jobs-table"">
            <thead>
                <tr>
                    <th style=""width: 80px;"">作业ID</th>
                    <th>文档名称</th>
                    <th style=""width: 150px;"">用户 / 客户端</th>
                    <th style=""width: 150px;"">打印机</th>
                    <th style=""width: 110px;"">文件大小</th>
                    <th style=""width: 140px;"">状态</th>
                    <th style=""width: 140px;"">提交时间</th>
                    <th style=""width: 180px;"">操作</th>
                </tr>
            </thead>
            <tbody id=""jobsTableBody"">
                <tr><td colspan=""8""><div class=""jobs-empty"">正在加载作业信息...</div></td></tr>
            </tbody>
        </table>
    </div>
</div>";
        }

        private string BuildPrintersSection(string baseUrl, int port)
        {
            return @"<div class=""card""><h2>打印机管理</h2><div class=""loading""><div class=""spinner""></div><p>正在加载打印机列表...</p></div></div>";
        }
        
        private string BuildTcpSection()
        {
            return @"<div class=""card""><h2>TCP 9100 打印服务管理</h2><div class=""loading""><div class=""spinner""></div><p>正在加载 TCP 服务配置...</p></div></div>";
        }

        private string BuildSystemSection()
        {
            var isRunning = _getIppServiceStateCallback?.Invoke() ?? true;
            var statusClass = isRunning ? "status-completed" : "status-stopped";
            var statusText = isRunning ? "运行中" : "已停止";
            return $@"
<div class=""card"">
    <h2>系统管理</h2>
    <div class=""system-tabs"">
        <div class=""tab-headers"">
            <button class=""tab-header active"" data-tab=""status"">服务状态</button>
            <button class=""tab-header"" data-tab=""settings"">系统参数</button>
            <button class=""tab-header"" data-tab=""restriction"">IP/MAC限制</button>
            <button class=""tab-header"" data-tab=""logs"">运行日志</button>
        </div>
        <div class=""tab-content"">
            <div id=""tab-status"" class=""tab-pane active"">
    <div class=""form-group"">
        <label>IPP 打印服务状态</label>
        <div style=""margin-top: 10px;"">
            <span id=""ippServiceStatus"" class=""status-badge {statusClass}"">{statusText}</span>
        </div>
    </div>
    <div class=""btn-group"">
        <button id=""btnStartService"" class=""btn btn-primary"" onclick=""controlService('start')"" {(isRunning ? "disabled" : "")}>启动服务</button>
        <button id=""btnStopService"" class=""btn btn-danger"" onclick=""controlService('stop')"" {(!isRunning ? "disabled" : "")}>停止服务</button>
        <button id=""btnRestartService"" class=""btn btn-warning"" onclick=""controlService('restart')"" {(!isRunning ? "disabled" : "")}>重启服务</button>
    </div>
    <div class=""form-group"" style=""margin-top: 30px;"">
        <label>TCP/IP打印服务状态</label>
        <div style=""margin-top: 10px;"">
            <span id=""tcpServiceStatus"" class=""status-badge status-stopped"">未知</span>
        </div>
    </div>
    <div class=""form-group"" style=""margin-top: 15px;"">
        <label style=""display: flex; align-items: center; cursor: pointer;"">
            <input type=""checkbox"" id=""chkEnableTcpWithIpp"" style=""margin-right: 8px; width: 18px; height: 18px; cursor: pointer;"" onchange=""updateTcpAutoStart(this.checked)"">
            <span>TCP/IP 打印服务随 IPP 打印服务启动</span>
        </label>
    </div>
    <div class=""btn-group"">
        <button id=""btnStartTcpService"" class=""btn btn-primary"" onclick=""controlTcpService('start')"">启动 TCP 服务</button>
        <button id=""btnStopTcpService"" class=""btn btn-danger"" onclick=""controlTcpService('stop')"">停止 TCP 服务</button>
    </div>
    <div style=""margin-top: 20px; padding: 10px; background: #f8f9fa; border-radius: 4px;"">
        <p id=""serviceStatusHint"" style=""margin: 0; color: #666; font-size: 12px;"">提示：停止服务后将拒绝对应的打印请求。</p>
        <p id=""serviceStatusMessage"" style=""margin: 5px 0 0 0; color: #007bff; font-size: 12px;""></p>
                </div>
            </div>
            <div id=""tab-settings"" class=""tab-pane"">
                <div class=""loading""><div class=""spinner""></div><p>正在加载系统参数...</p></div>
            </div>
            " + LoadRestrictionHtml() + @"
            <div id=""tab-logs"" class=""tab-pane"">
                <div class=""loading""><div class=""spinner""></div><p>正在加载运行日志...</p></div>
            </div>
        </div>
    </div>
</div>";
        }

        private string BuildAboutSection()
        {
            // 获取当前运行的地址信息
            ServerConfig currentConfig;
            lock (_configLock)
            {
                currentConfig = _config;
            }
            
            var localIps = _getLocalIpAddresses();
            var port = _extractPortFromPrefix(currentConfig.BasePrefix);
            var path = _extractPathFromPrefix(currentConfig.BasePrefix);
            var exampleIp = localIps.Count > 0 
                ? localIps.FirstOrDefault(ip => !ip.StartsWith("169.254.", StringComparison.Ordinal)) ?? localIps.FirstOrDefault() ?? "服务器IP"
                : "服务器IP";
            var exampleBaseUrl = $"http://{exampleIp}:{port}{path}";
            var exampleQueueUrl = $"{exampleBaseUrl.TrimEnd('/')}/队列名称";
            var exampleIppUrl = $"ipp://{exampleIp}:{port}{path.TrimEnd('/')}/队列名称";
            
            var sb = new StringBuilder();
            
            // 读取logo图片并转换为base64（优先从嵌入资源读取）
            string logoBase64 = "";
            try
            {
                var assembly = System.Reflection.Assembly.GetExecutingAssembly();
                // 尝试多种可能的资源名称格式
                var possibleNames = new[]
                {
                    "myself.png",
                    "IppServer.Host.WebUI.logo.png",
                    "WebUI.logo.png",
                    "logo.png"
                };
                
                string resourceName = null;
                foreach (var name in possibleNames)
                {
                    var fullName = assembly.GetManifestResourceNames().FirstOrDefault(n => n.EndsWith(name, StringComparison.OrdinalIgnoreCase));
                    if (fullName != null)
                    {
                        resourceName = fullName;
                        break;
                    }
                }
                
                if (resourceName != null)
                {
                    // 从嵌入资源读取
                    using (var stream = assembly.GetManifestResourceStream(resourceName))
                    {
                        if (stream != null)
                        {
                            using (var ms = new MemoryStream())
                            {
                                stream.CopyTo(ms);
                                logoBase64 = Convert.ToBase64String(ms.ToArray());
                            }
                        }
                    }
                }
                else
                {
                    // 如果嵌入资源不存在，尝试从文件系统读取（开发时使用）
                    var logoPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WebUI", "logo.png");
                    if (File.Exists(logoPath))
                    {
                        var logoData = File.ReadAllBytes(logoPath);
                        logoBase64 = Convert.ToBase64String(logoData);
                    }
                }
            }
            catch
            {
                // 忽略错误，logo将不显示
            }
            
            // 添加TAB标签式布局的CSS样式
            sb.AppendLine("<style>");
            sb.AppendLine(".about-container { width: 100% !important; max-width: 100% !important; margin: 0 !important; padding: 20px !important; box-sizing: border-box; }");
            sb.AppendLine("#about .about-container { width: 100% !important; max-width: 100% !important; }");
            sb.AppendLine(".about-tabs { display: flex; gap: 15px; margin-bottom: 25px; border-bottom: 3px solid #e0e0e0; }");
            sb.AppendLine(".about-tab { padding: 18px 36px; background: #f8f9fa; border: none; border-radius: 10px 10px 0 0; cursor: pointer; font-size: 18px; font-weight: 500; color: #666; transition: all 0.3s ease; position: relative; bottom: -3px; min-width: 120px; text-align: center; }");
            sb.AppendLine(".about-tab:hover { background: #e9ecef; color: #333; transform: translateY(-3px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }");
            sb.AppendLine(".about-tab.active { background: #fff; color: #007bff; border-bottom: 4px solid #007bff; font-weight: 700; box-shadow: 0 -3px 12px rgba(0,0,0,0.08); font-size: 19px; }");
            sb.AppendLine(".about-tab-content { display: none; animation: fadeIn 0.3s ease; }");
            sb.AppendLine(".about-tab-content.active { display: block; }");
            sb.AppendLine("@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }");
            sb.AppendLine(".about-card { background: #fff; border-radius: 12px; padding: 30px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); transition: all 0.3s ease; margin-bottom: 20px; }");
            sb.AppendLine(".about-card:hover { box-shadow: 0 4px 20px rgba(0,0,0,0.12); transform: translateY(-2px); }");
            sb.AppendLine(".about-logo { text-align: center; margin-bottom: 30px; }");
            sb.AppendLine(".about-logo img { max-width: 120px; height: auto; filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1)); transition: transform 0.3s ease; }");
            sb.AppendLine(".about-logo img:hover { transform: scale(1.05); }");
            sb.AppendLine("</style>");
            
            sb.AppendLine("<div class=\"about-container\">");
            
            // Logo和标题
            sb.AppendLine("    <div class=\"about-logo\">");
            if (!string.IsNullOrEmpty(logoBase64))
            {
                sb.AppendLine($"        <img src=\"data:image/png;base64,{logoBase64}\" alt=\"IPP Server Logo\" />");
            }
            sb.AppendLine("        <h2 style=\"margin: 20px 0 10px 0; font-size: 32px; color: #2c3e50; font-weight: 700; text-align: center;\">关于系统</h2>");
            sb.AppendLine($"        <p style=\"margin: 0; color: #666; font-size: 14px; text-align: center;\">IPP Server © {DateTime.Now.Year}</p>");
            sb.AppendLine("    </div>");
            
            // TAB标签
            sb.AppendLine("    <div class=\"about-tabs\">");
            sb.AppendLine("        <button class=\"about-tab active\" onclick=\"switchAboutTab('intro')\">📋 功能说明</button>");
            sb.AppendLine("        <button class=\"about-tab\" onclick=\"switchAboutTab('features')\">✨ 主要功能</button>");
            sb.AppendLine("        <button class=\"about-tab\" onclick=\"switchAboutTab('usage')\">📖 使用方法</button>");
            sb.AppendLine("        <button class=\"about-tab\" onclick=\"switchAboutTab('author')\">👤 作者信息</button>");
            sb.AppendLine("    </div>");
            
            // TAB内容区域
            sb.AppendLine("    <div class=\"about-tab-content active\" id=\"about-intro\">");
            sb.AppendLine("        <div class=\"about-card\">");
            sb.AppendLine("            <h3 style=\"margin: 0 0 20px 0; font-size: 22px; color: #2c3e50; font-weight: 600; padding-bottom: 15px; border-bottom: 2px solid #e0e0e0;\">📋 功能说明</h3>");
            sb.AppendLine("            <p style=\"margin: 0; line-height: 2; color: #555; font-size: 16px;\">IPP Server 是一个基于 .NET Framework 的 IPP 打印服务器，可以将本地 USB/本地打印机通过标准 IPP 协议暴露给局域网客户端，实现网络打印共享功能。</p>");
            sb.AppendLine("        </div>");
            sb.AppendLine("    </div>");
            
            sb.AppendLine("    <div class=\"about-tab-content\" id=\"about-features\">");
            sb.AppendLine("        <div class=\"about-card\">");
            sb.AppendLine("            <h3 style=\"margin: 0 0 20px 0; font-size: 22px; color: #2c3e50; font-weight: 600; padding-bottom: 15px; border-bottom: 2px solid #e0e0e0;\">✨ 主要功能</h3>");
            sb.AppendLine("            <ul style=\"margin: 0; padding-left: 25px; line-height: 2.5; color: #555; font-size: 16px;\">");
            sb.AppendLine($"                <li style=\"margin-bottom: 12px;\">单端口多打印机路径：<code style=\"background: #f5f5f5; padding: 4px 8px; border-radius: 4px; font-family: 'Consolas', 'Courier New', monospace; color: #d63384; font-size: 14px;\">{WebUtility.HtmlEncode(exampleQueueUrl)}</code></li>");
            sb.AppendLine("                <li style=\"margin-bottom: 12px;\">GUI 控制台可扫描本机打印机、选择并生成共享队列</li>");
            sb.AppendLine("                <li style=\"margin-bottom: 12px;\">IPP Print-Job 请求自动投递至所选本地打印机</li>");
            sb.AppendLine("                <li style=\"margin-bottom: 12px;\">支持 mDNS 自动发现服务，客户端可自动发现打印机</li>");
            sb.AppendLine("                <li style=\"margin-bottom: 12px;\">提供专业的 Web 管理界面，支持远程配置和监控</li>");
            sb.AppendLine("            </ul>");
            sb.AppendLine("        </div>");
            sb.AppendLine("    </div>");
            
            sb.AppendLine("    <div class=\"about-tab-content\" id=\"about-usage\">");
            sb.AppendLine("        <div class=\"about-card\">");
            sb.AppendLine("            <h3 style=\"margin: 0 0 20px 0; font-size: 22px; color: #2c3e50; font-weight: 600; padding-bottom: 15px; border-bottom: 2px solid #e0e0e0;\">📖 使用方法</h3>");
            sb.AppendLine("            <ol style=\"margin: 0; padding-left: 25px; line-height: 2.5; color: #555; font-size: 16px;\">");
            sb.AppendLine("                <li style=\"margin-bottom: 15px;\">方式一：在 Windows <strong>\"添加打印机\"</strong>向导中选择 <strong style=\"color: #007bff;\">\"我所需要的打印机未列出\"</strong>并点击下一步进入自定义添加打印机窗口。</li>");
            sb.AppendLine($"                <li style=\"margin-bottom: 15px;\">然后选择<strong>\"按名称选择共享打印机\"</strong>，在编辑框中输入 HTTP URI：<code style=\"background: #f5f5f5; padding: 4px 8px; border-radius: 4px; font-family: 'Consolas', 'Courier New', monospace; color: #d63384; font-size: 14px;\">{WebUtility.HtmlEncode(exampleQueueUrl)}</code>，或输入 IPP URI：<code style=\"background: #f5f5f5; padding: 4px 8px; border-radius: 4px; font-family: 'Consolas', 'Courier New', monospace; color: #d63384; font-size: 14px;\">{WebUtility.HtmlEncode(exampleIppUrl)}</code>,选择对应驱动完成打印机安装。</li>");
            sb.AppendLine("                <li style=\"margin-bottom: 15px;\">方式二：在客户端安装并运行 <strong>客户端辅助工具</strong>，通过向导自动发现并添加打印机。<a href=\"clienttools/ipp-printer-install-helper.exe\" style=\"display:inline-block;margin-left:12px;padding:6px 14px;background:#007bff;color:#fff;border-radius:4px;text-decoration:none;font-weight:500;font-size:14px;\">下载客户端辅助工具</a></li>");
            sb.AppendLine("                <li style=\"margin-bottom: 15px;\">客户端需安装对应打印机驱动，与服务端保持一致</li>");
            sb.AppendLine("            </ol>");
            sb.AppendLine("            <div style=\"margin-top: 25px; padding: 20px; background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%); border-left: 4px solid #2196f3; border-radius: 8px; box-shadow: 0 2px 8px rgba(33,150,243,0.1);\">");
            sb.AppendLine("                <p style=\"margin: 0; line-height: 2; color: #555; font-size: 15px;\"><strong style=\"color: #1976d2;\">💡 提示：</strong>将上述 URI 中的 <code style=\"background: #fff; padding: 3px 6px; border-radius: 3px; font-size: 13px;\">队列名称</code> 替换为实际的队列名称，<code style=\"background: #fff; padding: 3px 6px; border-radius: 3px; font-size: 13px;\">服务器IP</code> 替换为实际的服务器 IP 地址。</p>");
            sb.AppendLine("            </div>");
            sb.AppendLine("        </div>");
            sb.AppendLine("    </div>");
            
            sb.AppendLine("    <div class=\"about-tab-content\" id=\"about-author\">");
            sb.AppendLine("        <div class=\"about-card\" style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; box-shadow: 0 4px 20px rgba(102,126,234,0.3);\">");
            sb.AppendLine("            <h3 style=\"margin: 0 0 25px 0; font-size: 22px; font-weight: 600; padding-bottom: 15px; border-bottom: 2px solid rgba(255,255,255,0.3);\">👤 作者信息</h3>");
            sb.AppendLine("            <div style=\"line-height: 2.5; font-size: 16px;\">");
            sb.AppendLine("                <p style=\"margin: 0 0 15px 0;\"><strong>作者：</strong>草原企鹅</p>");
            sb.AppendLine("                <p style=\"margin: 0 0 15px 0;\"><strong>微信：</strong>15242609</p>");
            sb.AppendLine("                <p style=\"margin: 0;\"><strong>邮箱：</strong><a href=\"mailto:tyq@qq.com\" style=\"color: #fff; text-decoration: underline; font-weight: 500;\">tyq@qq.com</a></p>");
            sb.AppendLine("            </div>");
            sb.AppendLine("        </div>");
            sb.AppendLine("    </div>");
            
            // 版权信息
            sb.AppendLine("    <div style=\"text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #e0e0e0; color: #999; font-size: 13px;\">");
            sb.AppendLine($"        <p style=\"margin: 0;\">IPP Server © {DateTime.Now.Year} | All Rights Reserved</p>");
            sb.AppendLine("    </div>");
            
            // TAB切换JavaScript
            sb.AppendLine("    <script>");
            sb.AppendLine("    function switchAboutTab(tabId) {");
            sb.AppendLine("        // 隐藏所有TAB内容");
            sb.AppendLine("        document.querySelectorAll('.about-tab-content').forEach(content => {");
            sb.AppendLine("            content.classList.remove('active');");
            sb.AppendLine("        });");
            sb.AppendLine("        // 移除所有TAB按钮的激活状态");
            sb.AppendLine("        document.querySelectorAll('.about-tab').forEach(tab => {");
            sb.AppendLine("            tab.classList.remove('active');");
            sb.AppendLine("        });");
            sb.AppendLine("        // 显示选中的TAB内容");
            sb.AppendLine("        var contentId = 'about-' + tabId;");
            sb.AppendLine("        var content = document.getElementById(contentId);");
            sb.AppendLine("        if (content) {");
            sb.AppendLine("            content.classList.add('active');");
            sb.AppendLine("        }");
            sb.AppendLine("        // 激活对应的TAB按钮");
            sb.AppendLine("        event.target.classList.add('active');");
            sb.AppendLine("    }");
            sb.AppendLine("    </script>");
            
            sb.AppendLine("</div>");
            
            return sb.ToString();
        }

        private string BuildAdminJs()
        {
            return @"
<script>
function logout() {
    if (!confirm('确定要退出登录吗？')) {
        return;
    }
    
    var apiBase = window.location.pathname.replace(/\/admin.*$/, '') + '/api/auth';
    fetch(apiBase + '/logout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' }
    })
    .then(function(response) {
        return response.json();
    })
    .then(function(data) {
        if (data.success) {
            // 清除Cookie并跳转到登录页面
            document.cookie = 'ipp_admin_session=; Path=/; Max-Age=0';
            window.location.href = window.location.pathname.replace(/\/admin.*$/, '/admin') || '/admin';
        } else {
            alert('退出登录失败：' + (data.message || '未知错误'));
        }
    })
    .catch(function(error) {
        console.error('退出登录失败：', error);
        // 即使API调用失败，也清除Cookie并跳转
        document.cookie = 'ipp_admin_session=; Path=/; Max-Age=0';
        window.location.href = window.location.pathname.replace(/\/admin.*$/, '/admin') || '/admin';
    });
}

function showSection(sectionId, clickedItem) {
    // 隐藏所有内容区域
    document.querySelectorAll('.content-section').forEach(s => s.classList.remove('active'));
    // 移除所有侧边栏项的激活状态
    document.querySelectorAll('.sidebar-item').forEach(s => s.classList.remove('active'));
    // 显示选中的内容区域
    const targetSection = document.getElementById(sectionId);
    if (targetSection) {
        targetSection.classList.add('active');
    }
    // 激活选中的侧边栏项
    if (clickedItem) {
        clickedItem.classList.add('active');
    }
    
    // 根据目标区域触发数据刷新
    if (sectionId === 'printers') {
        setTimeout(loadPrinters, 100);
    }
    if (sectionId === 'jobs') {
        setTimeout(loadJobs, 100);
    }
    if (sectionId === 'tcp') {
        setTimeout(loadTcpTasks, 100);
    }
    if (sectionId === 'system') {
        initSystemTabs();
    }
}

document.querySelectorAll('.sidebar-item').forEach(item => {
    item.addEventListener('click', function(e) {
        e.preventDefault();
        e.stopPropagation();
        const sectionId = this.getAttribute('data-section');
        if (sectionId) {
            showSection(sectionId, this);
        }
    });
});

let jobsCache = [];
let currentJobTab = 'all';

// 统计卡片点击导航
document.querySelectorAll('.stat-card[data-target]').forEach(card => {
    card.addEventListener('click', function() {
        const target = this.getAttribute('data-target');
        if (!target) return;
        const sidebarItem = document.querySelector('.sidebar-item[data-section=""' + target + '""]');
        showSection(target, sidebarItem);
    });
});

function copyToClipboard(text) {
    if (!text) {
        alert('没有可复制的内容');
        return;
    }
    // 尝试使用现代 Clipboard API
    if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard.writeText(text).then(() => {
        alert('已复制到剪贴板：' + text);
    }).catch(err => {
            // 如果 Clipboard API 失败，使用备用方法
            fallbackCopyToClipboard(text);
        });
    } else {
        // 备用方法：使用传统的 execCommand
        fallbackCopyToClipboard(text);
    }
}

function fallbackCopyToClipboard(text) {
    var textArea = document.createElement('textarea');
    textArea.value = text;
    textArea.style.position = 'fixed';
    textArea.style.left = '-999999px';
    textArea.style.top = '-999999px';
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();
    try {
        var successful = document.execCommand('copy');
        if (successful) {
            alert('已复制到剪贴板：' + text);
        } else {
            alert('复制失败，请手动复制：' + text);
        }
    } catch (err) {
        alert('复制失败，请手动复制：' + text);
    }
    document.body.removeChild(textArea);
}

function testPrint(uri, btn) {
    if (!uri) {
        alert('无效的URI地址');
        return;
    }
    
    var originalText = btn.textContent;
    var originalDisabled = btn.disabled;
    btn.disabled = true;
    btn.textContent = '测试中...';
    
    var testUrl = uri + (uri.indexOf('?') === -1 ? '?' : '&') + 'test=1&format=json';
    
    fetch(testUrl, {
        method: 'GET',
        headers: {
            'Accept': 'application/json'
        }
    })
    .then(function(response) {
        if (!response.ok) {
            throw new Error('HTTP ' + response.status);
        }
        return response.json();
    })
    .then(function(data) {
        btn.disabled = originalDisabled;
        btn.textContent = originalText;
        if (data.success) {
            alert('测试打印成功：' + data.message);
        } else {
            alert('测试打印失败：' + data.message);
        }
    })
    .catch(function(error) {
        btn.disabled = originalDisabled;
        btn.textContent = originalText;
        alert('测试打印失败：' + (error.message || '未知错误'));
    });
}

function initSystemTabs() {
    document.querySelectorAll('.tab-header').forEach(header => {
        header.addEventListener('click', function() {
            var tabId = this.getAttribute('data-tab');
            if (!tabId) return;
            
            // 切换TAB头部
            document.querySelectorAll('.tab-header').forEach(h => h.classList.remove('active'));
            this.classList.add('active');
            
            // 切换TAB内容
            document.querySelectorAll('.tab-pane').forEach(p => {
                p.classList.remove('active');
                p.style.display = 'none';
            });
            var pane = document.getElementById('tab-' + tabId);
            if (pane) {
                pane.classList.add('active');
                pane.style.display = 'block';
                
                // 根据TAB加载数据
                if (tabId === 'settings') {
                    loadSystemSettings();
                } else if (tabId === 'logs') {
                    loadSystemLogs();
                } else if (tabId === 'status') {
                    loadServiceStatus();
                } else if (tabId === 'restriction') {
                    // 延迟一下确保DOM已更新
                    setTimeout(function() {
                        if (typeof loadRestriction === 'function') {
                            loadRestriction();
                        } else {
                            console.error('loadRestriction 函数未定义');
                        }
                    }, 50);
                }
            }
        });
    });
}

function updateServiceControls(isRunning) {
    var badge = document.getElementById('ippServiceStatus');
    if (badge) {
        badge.textContent = isRunning ? '运行中' : '已停止';
        badge.classList.remove('status-completed', 'status-stopped');
        badge.classList.add(isRunning ? 'status-completed' : 'status-stopped');
    }
    var btnStart = document.getElementById('btnStartService');
    var btnStop = document.getElementById('btnStopService');
    var btnRestart = document.getElementById('btnRestartService');
    if (btnStart) btnStart.disabled = isRunning;
    if (btnStop) btnStop.disabled = !isRunning;
    if (btnRestart) btnRestart.disabled = !isRunning;
}

function loadServiceStatus() {
    fetch(getSystemApiBase() + '/status')
        .then(function(response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(function(data) {
            var isRunning = data.ippServiceRunning !== false;
            updateServiceControls(isRunning);
            
            // 更新 TCP 服务状态
            var tcpRunning = data.tcpServiceRunning === true;
            updateTcpServiceControls(tcpRunning);
            
            // 更新 TCP 随 IPP 启动复选框
            if (data.enableTcp9100Service !== undefined) {
                var chk = document.getElementById('chkEnableTcpWithIpp');
                if (chk) {
                    chk.checked = data.enableTcp9100Service === true;
                }
            }
            
            var hint = document.getElementById('serviceStatusMessage');
            if (hint) {
                hint.textContent = '';
            }
        })
        .catch(function(error) {
            console.error('加载服务状态失败：', error);
            var hint = document.getElementById('serviceStatusMessage');
            if (hint) {
                hint.textContent = '无法获取服务状态：' + error.message;
            }
        });
}

function updateTcpAutoStart(checked) {
    var statusMsg = document.getElementById('serviceStatusMessage');
    if (statusMsg) {
        statusMsg.textContent = '正在更新设置...';
        statusMsg.style.color = '#007bff';
    }
    
    // 加载当前设置
    fetch(getSystemApiBase() + '/settings')
        .then(function(response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(function(data) {
            // 更新 enableTcp9100Service 设置
            data.enableTcp9100Service = checked;
            
            // 保存设置
            return fetch(getSystemApiBase() + '/settings', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(data)
            });
        })
        .then(function(response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(function(result) {
            if (statusMsg) {
                statusMsg.textContent = result.message || '设置已保存';
                statusMsg.style.color = '#28a745';
            }
            // 如果取消勾选且TCP服务正在运行，停止TCP服务
            if (!checked) {
                var tcpRunning = document.getElementById('tcpServiceStatus');
                if (tcpRunning && tcpRunning.textContent === '运行中') {
                    setTimeout(function() {
                        controlTcpService('stop');
                    }, 500);
                }
            }
        })
        .catch(function(error) {
            console.error('更新设置失败：', error);
            if (statusMsg) {
                statusMsg.textContent = '更新设置失败：' + error.message;
                statusMsg.style.color = '#dc3545';
            }
            // 恢复复选框状态
            var chk = document.getElementById('chkEnableTcpWithIpp');
            if (chk) {
                chk.checked = !checked;
            }
        });
}

function updateTcpServiceControls(isRunning) {
    var badge = document.getElementById('tcpServiceStatus');
    if (badge) {
        badge.textContent = isRunning ? '运行中' : '已停止';
        badge.classList.remove('status-completed', 'status-stopped');
        badge.classList.add(isRunning ? 'status-completed' : 'status-stopped');
    }
    var btnStart = document.getElementById('btnStartTcpService');
    var btnStop = document.getElementById('btnStopTcpService');
    if (btnStart) btnStart.disabled = isRunning;
    if (btnStop) btnStop.disabled = !isRunning;
}

function controlTcpService(action) {
    if (!action) return;
    
    var statusMsg = document.getElementById('serviceStatusMessage');
    if (statusMsg) {
        statusMsg.textContent = '正在执行 TCP 服务 ' + action + ' 操作...';
    }
    
    document.querySelectorAll('#tab-status .btn-group button[id*=""Tcp""]').forEach(btn => btn.disabled = true);
    
    var apiPath = getSystemApiBase() + '/tcp/' + action;
    fetch(apiPath, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' }
    })
    .then(function(response) {
        return response.json();
    })
    .then(function(data) {
        if (statusMsg) {
            statusMsg.textContent = data.message || (data.success ? '操作成功' : '操作失败');
            statusMsg.style.color = data.success ? '#28a745' : '#dc3545';
        }
        if (data.success) {
            setTimeout(function() {
                loadServiceStatus();
            }, 500);
        }
    })
    .catch(function(error) {
        if (statusMsg) {
            statusMsg.textContent = '操作失败：' + error.message;
            statusMsg.style.color = '#dc3545';
        }
    })
    .finally(function() {
        document.querySelectorAll('#tab-status .btn-group button[id*=""Tcp""]').forEach(btn => btn.disabled = false);
        setTimeout(function() {
            loadServiceStatus();
        }, 1000);
    });
}

function loadSystemSettings() {
    var container = document.getElementById('tab-settings');
    if (!container) return;
    
    container.innerHTML = '<div class=""loading""><div class=""spinner""></div><p>正在加载系统参数...</p></div>';
    
    fetch(getSystemApiBase() + '/settings')
        .then(function(response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(function(data) {
            renderSystemSettings(data, container);
        })
        .catch(function(error) {
            container.innerHTML = '<div class=""error"">加载系统参数失败：' + error.message + '</div>';
        });
}

function renderSystemSettings(data, container) {
    var html = '<form id=""system-settings-form"">';
    
    // 监听网卡
    html += '<div class=""form-group"">';
    html += '<label>监听网卡</label>';
    html += '<select id=""setting-interface"" name=""interface"">';
    html += '<option value=""+"">所有网卡 (0.0.0.0)</option>';
    if (data.interfaces && data.interfaces.length > 0) {
        data.interfaces.forEach(function(iface) {
            var selected = data.interface === iface.value ? ' selected' : '';
            html += '<option value=""' + escapeHtml(iface.value) + '""' + selected + '>' + escapeHtml(iface.label) + '</option>';
        });
    }
    html += '</select>';
    html += '<div class=""help-text"">选择要监听的网络接口，选择""所有网卡""将监听所有接口</div>';
    html += '</div>';
    
    // 监听端口
    html += '<div class=""form-group"">';
    html += '<label>监听端口</label>';
    html += '<input type=""number"" id=""setting-port"" name=""port"" min=""1"" max=""65535"" value=""' + (data.port || 8631) + '"">';
    html += '<div class=""help-text"">端口范围：1 - 65535</div>';
    html += '</div>';
    
    // 访问路径
    html += '<div class=""form-group"">';
    html += '<label>访问路径</label>';
    html += '<input type=""text"" id=""setting-path"" name=""path"" value=""' + escapeHtml(data.path || '/printers/') + '"">';
    html += '<div class=""help-text"">打印机队列的访问路径，必须以 / 开头和结尾</div>';
    html += '</div>';
    
    // 启动参数
    html += '<div class=""form-group"">';
    html += '<label>启动参数</label>';
    html += '<div class=""checkbox-group"">';
    html += '<input type=""checkbox"" id=""setting-auto-start"" name=""autoStart""' + (data.autoStart ? ' checked' : '') + '>';
    html += '<label for=""setting-auto-start"">启动后自动开启服务</label>';
    html += '</div>';
    html += '<div class=""checkbox-group"">';
    html += '<input type=""checkbox"" id=""setting-run-on-boot"" name=""runOnBoot""' + (data.runOnBoot ? ' checked' : '') + '>';
    html += '<label for=""setting-run-on-boot"">开启自启动</label>';
    html += '</div>';
    html += '<div class=""checkbox-group"">';
    html += '<input type=""checkbox"" id=""setting-enable-mdns"" name=""enableMdns""' + (data.enableMdns ? ' checked' : '') + '>';
    html += '<label for=""setting-enable-mdns"">开启mDNS发现服务</label>';
    html += '</div>';
    html += '</div>';
    
    // 管理员密码修改
    html += '<div class=""form-group"" style=""margin-top: 30px; padding-top: 20px; border-top: 2px solid #e0e0e0;"">';
    html += '<label style=""font-size: 16px; font-weight: 600; color: #333; margin-bottom: 15px;"">🔐 管理员密码</label>';
    html += '<div style=""background: #f8f9fa; padding: 15px; border-radius: 6px; margin-bottom: 15px;"">';
    html += '<div style=""color: #666; font-size: 13px; margin-bottom: 10px;"">';
    if (data.hasAdminPassword) {
        html += '<span style=""color: #28a745; font-weight: 500;"">✓ 已设置密码</span>';
    } else {
        html += '<span style=""color: #ffc107; font-weight: 500;"">⚠ 未设置密码（首次登录可使用任意密码）</span>';
    }
    html += '</div>';
    html += '<div class=""form-group"" style=""margin-bottom: 0;"">';
    html += '<label for=""setting-admin-password"" style=""font-size: 14px; font-weight: normal;"">新密码（留空则不修改，至少6个字符）</label>';
    html += '<input type=""password"" id=""setting-admin-password"" name=""adminPassword"" placeholder=""请输入新密码"" minlength=""6"">';
    html += '<div class=""help-text"">密码长度至少为6个字符，建议使用强密码</div>';
    html += '</div>';
    html += '</div>';
    html += '</div>';
    
    html += '<div class=""btn-group"" style=""margin-top: 20px;"">';
    html += '<button type=""submit"" class=""btn btn-primary"">保存设置</button>';
    html += '<button type=""button"" class=""btn"" onclick=""loadSystemSettings()"">重置</button>';
    html += '</div>';
    html += '</form>';
    
                container.innerHTML = html;
    
    // 绑定表单提交事件
    var form = document.getElementById('system-settings-form');
    if (form) {
        form.addEventListener('submit', function(e) {
            e.preventDefault();
            saveSystemSettings();
        });
    }
}

function saveSystemSettings() {
    var form = document.getElementById('system-settings-form');
    if (!form) return;
    
    // 记录当前的端口和路径
    var currentUrl = window.location;
    var currentPort = currentUrl.port || (currentUrl.protocol === 'https:' ? 443 : 80);
    var currentPath = currentUrl.pathname;
    
    // 提取当前路径前缀（/printers/ 或 /ipp/ 等）
    // 从路径中提取第一个路径段作为前缀
    var currentPathPrefix = '/printers/'; // 默认值
    var pathMatch = currentPath.match(/^\/([^\/]+)\//);
    if (pathMatch) {
        currentPathPrefix = '/' + pathMatch[1] + '/';
            } else {
        // 如果没有匹配，检查是否是根路径或只有一层路径
        var pathParts = currentPath.split('/').filter(function(p) { return p; });
        if (pathParts.length > 0) {
            // 如果路径是 /admin 这样的格式，说明可能没有前缀，使用默认值
            // 但如果路径是 /printers 或 /ipp，则提取它
            if (pathParts[0] === 'printers' || pathParts[0] === 'ipp') {
                currentPathPrefix = '/' + pathParts[0] + '/';
            }
        }
    }
    
    var formData = new FormData(form);
    var adminPassword = document.getElementById('setting-admin-password').value;
    var data = {
        interface: document.getElementById('setting-interface').value,
        port: parseInt(document.getElementById('setting-port').value),
        path: document.getElementById('setting-path').value,
        autoStart: document.getElementById('setting-auto-start').checked,
        runOnBoot: document.getElementById('setting-run-on-boot').checked,
        enableMdns: document.getElementById('setting-enable-mdns').checked
    };
    
    // 如果输入了新密码，添加到数据中
    if (adminPassword && adminPassword.trim()) {
        if (adminPassword.length < 6) {
            alert('密码长度至少为6个字符');
            return;
        }
        data.adminPassword = adminPassword;
    }
    
    // 验证
    if (data.port < 1 || data.port > 65535) {
        alert('端口范围必须在 1 - 65535 之间');
        return;
    }
    if (!data.path.startsWith('/') || !data.path.endsWith('/')) {
        alert('访问路径必须以 / 开头和结尾');
        return;
    }
    
    fetch(getSystemApiBase() + '/settings', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    })
    .then(function(response) {
        if (!response.ok) {
            return response.json().then(function(err) {
                throw new Error(err.message || '保存失败');
            });
        }
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            // 检查端口或路径是否改变
            var portChanged = parseInt(currentPort) !== data.port;
            var pathChanged = currentPathPrefix !== data.path;
            
            if (portChanged || pathChanged) {
                // 使用API返回的新信息构建URL
                var newHost = currentUrl.hostname;
                var newPort = result.newPort || data.port;
                var newPath = result.newPath || data.path;
                
                // 提取当前路径的剩余部分
                // 例如：/printers/admin -> admin, /printers/hp1018 -> hp1018, /printers/ -> ''
                var remainingPath = '';
                
                // 先尝试匹配当前路径前缀
                var escapedPrefix = currentPathPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                var currentPathMatch = currentPath.match(new RegExp('^' + escapedPrefix + '(.+)$'));
                if (currentPathMatch && currentPathMatch[1]) {
                    remainingPath = currentPathMatch[1];
                } else if (currentPath === currentPathPrefix || currentPath === currentPathPrefix.replace(/\/$/, '')) {
                    // 如果当前路径就是前缀本身（如 /printers/ 或 /printers），没有剩余部分
                    remainingPath = '';
                } else {
                    // 尝试提取最后一个路径段作为fallback
                    var pathParts = currentPath.split('/').filter(function(p) { return p && p !== 'printers' && p !== 'ipp'; });
                    if (pathParts.length > 0) {
                        remainingPath = pathParts[pathParts.length - 1];
                    }
                }
                
                // 构建新的完整路径
                var newFullPath = newPath;
                if (remainingPath) {
                    // 移除路径末尾的/，添加剩余路径
                    newFullPath = newPath.replace(/\/$/, '') + '/' + remainingPath;
                } else {
                    // 如果没有剩余路径，默认跳转到admin页面
                    newFullPath = newPath.replace(/\/$/, '') + '/admin';
                }
                
                // 构建新的完整URL
                var newUrl = currentUrl.protocol + '//' + newHost + ':' + newPort + newFullPath;
                
                alert('设置已保存并已同步到GUI界面，配置已重载。页面将自动刷新到新地址：' + newUrl);
                
                // 跳转到新URL
                window.location.href = newUrl;
            } else {
                alert('设置已保存并已同步到GUI界面，配置已重载');
                // 重新加载系统设置和概览页面数据
                loadSystemSettings();
                // 如果当前在概览页面，刷新概览数据
                if (document.getElementById('overview') && document.getElementById('overview').classList.contains('active')) {
                    location.reload();
                }
            }
        } else {
            alert('保存失败：' + (result.message || '未知错误'));
        }
    })
    .catch(function(error) {
        // 检查端口或路径是否改变
        var portChanged = parseInt(currentPort) !== data.port;
        var pathChanged = currentPathPrefix !== data.path;
        
        if (portChanged || pathChanged) {
            // 即使请求失败，如果端口或路径改变了，配置可能已经保存成功
            // 尝试跳转到新地址（如果配置已保存，服务会重启，新地址应该可用）
            var newHost = currentUrl.hostname;
            var newPort = data.port;
            var newPath = data.path;
            
            // 提取当前路径的剩余部分
            var remainingPath = '';
            var escapedPrefix = currentPathPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            var currentPathMatch = currentPath.match(new RegExp('^' + escapedPrefix + '(.+)$'));
            if (currentPathMatch && currentPathMatch[1]) {
                remainingPath = currentPathMatch[1];
            } else if (currentPath === currentPathPrefix || currentPath === currentPathPrefix.replace(/\/$/, '')) {
                remainingPath = '';
            } else {
                var pathParts = currentPath.split('/').filter(function(p) { return p && p !== 'printers' && p !== 'ipp'; });
                if (pathParts.length > 0) {
                    remainingPath = pathParts[pathParts.length - 1];
                }
            }
            
            // 构建新的完整路径
            var newFullPath = newPath;
            if (remainingPath) {
                newFullPath = newPath.replace(/\/$/, '') + '/' + remainingPath;
            } else {
                newFullPath = newPath.replace(/\/$/, '') + '/admin';
            }
            
            // 构建新的完整URL
            var newUrl = currentUrl.protocol + '//' + newHost + ':' + newPort + newFullPath;
            
            // 延迟一下再跳转，给服务重启时间
            // 配置很可能已经保存成功，服务正在重启，直接尝试跳转
            setTimeout(function() {
                console.log('尝试跳转到新地址：' + newUrl);
                window.location.href = newUrl;
            }, 1500); // 延迟1.5秒，给服务重启时间
        } else {
            alert('保存失败：' + error.message);
        }
    });
}

function loadSystemLogs() {
    var container = document.getElementById('tab-logs');
    if (!container) return;
    
    container.innerHTML = '<div class=""loading""><div class=""spinner""></div><p>正在加载运行日志...</p></div>';
    
    fetch(getSystemApiBase() + '/logs')
        .then(function(response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(function(data) {
            renderSystemLogs(data, container);
        })
        .catch(function(error) {
            container.innerHTML = '<div class=""error"">加载运行日志失败：' + error.message + '</div>';
        });
}

function renderSystemLogs(data, container) {
    var html = '<div style=""margin-bottom: 15px;"">';
    html += '<button class=""btn btn-primary"" onclick=""exportLogs()"">导出日志</button>';
    html += '<button class=""btn btn-danger"" onclick=""clearLogs()"" style=""margin-left: 10px;"">清除日志</button>';
    html += '</div>';
    
    // 黑底绿字终端风格
    html += '<div class=""log-container"" style=""max-height: 500px; overflow-y: auto; background: #000000; padding: 15px; border-radius: 4px; font-family: ""Consolas"", ""Courier New"", monospace; font-size: 13px; color: #00ff00; line-height: 1.6;"">';
    if (data.logs && data.logs.length > 0) {
        data.logs.forEach(function(log) {
            var timestamp = log.timestamp || '';
            var message = log.message || log || '';
            html += '<div class=""log-entry"" style=""margin-bottom: 2px; padding: 2px 0;"">';
            if (timestamp) {
                html += '<span style=""color: #00ff00; margin-right: 10px;"">[' + escapeHtml(timestamp) + ']</span>';
            }
            html += '<span style=""color: #00ff00;"">' + escapeHtml(message) + '</span>';
            html += '</div>';
        });
    } else {
        html += '<div class=""log-entry"" style=""padding: 10px; text-align: center; color: #00ff00;"">暂无日志记录</div>';
    }
    html += '</div>';
    
    container.innerHTML = html;
}

function exportLogs() {
    window.location.href = getSystemApiBase() + '/logs/export';
}

function clearLogs() {
    if (!confirm('确定要清除所有运行日志吗？此操作不可恢复。')) {
        return;
    }
    
    fetch(getSystemApiBase() + '/logs', {
        method: 'DELETE'
    })
    .then(function(response) {
        if (!response.ok) {
            throw new Error('HTTP ' + response.status);
        }
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            alert('日志已清除');
            loadSystemLogs();
        } else {
            alert('清除失败：' + (result.message || '未知错误'));
        }
    })
    .catch(function(error) {
        alert('清除失败：' + error.message);
    });
}

function escapeHtml(text) {
    if (!text) return '';
    var div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// 全局按钮事件委托，避免在HTML中内联脚本
document.addEventListener('click', function(e) {
    var btn = e.target.closest('[data-action]');
    if (!btn) {
        return;
    }
    
    var action = btn.getAttribute('data-action');
    if (!action) {
        return;
    }
    
    if (btn.closest('.printer-card')) {
        e.stopPropagation();
    }
    e.preventDefault();
    
    var printerName = btn.getAttribute('data-printer');
    var queueName = btn.getAttribute('data-queue');
    var uri = btn.getAttribute('data-uri');
    
    switch (action) {
        case 'copy-uri':
            if (uri) {
                copyToClipboard(uri);
            }
            break;
        case 'edit-queue':
            if (printerName && queueName) {
                showEditQueueDialog(printerName, queueName);
            }
            break;
        case 'remove-queue':
            if (printerName) {
                removePrinterQueue(printerName);
            }
            break;
        case 'add-queue':
            if (printerName) {
                showAddQueueDialog(printerName);
            }
            break;
        case 'confirm-edit-queue':
            confirmEditQueue(btn);
            break;
        case 'stop-job':
            stopJob(btn.getAttribute('data-job-id'));
            break;
        case 'reprint-job':
            reprintJob(btn.getAttribute('data-job-id'));
            break;
        case 'delete-job':
            deleteJob(btn.getAttribute('data-job-id'));
            break;
        case 'test-print':
            var testUri = btn.getAttribute('data-uri');
            if (testUri) {
                testPrint(testUri, btn);
            }
            break;
    }
});

function getApiBase() {
    // 动态获取当前路径前缀（如 /printers/, /ipp/ 等）
    var pathname = window.location.pathname;
    var pathMatch = pathname.match(/^\/([^\/]+)\//);
    if (pathMatch && pathMatch[1]) {
        var prefix = '/' + pathMatch[1] + '/';
        return prefix + 'api';
    }
    // 如果没有匹配到路径前缀，使用默认的 /api
    return '/api';
}

function getJobsApiBase() {
    return getApiBase() + '/jobs';
}

function getSystemApiBase() {
    return getApiBase() + '/system';
}

function getPrintersApiBase() {
    return getApiBase() + '/printers';
}

function initJobTabs() {
    document.querySelectorAll('.job-tab').forEach(tab => {
        tab.addEventListener('click', function() {
            document.querySelectorAll('.job-tab').forEach(t => t.classList.remove('active'));
            this.classList.add('active');
            currentJobTab = this.getAttribute('data-job-tab') || 'all';
            renderJobsTable();
        });
    });
}

function loadJobs() {
    // 同时加载 IPP 作业和 TCP 作业
    Promise.all([
        fetch(getJobsApiBase()).then(r => r.ok ? r.json() : { jobs: [], summary: {} }),
        fetch(getTcpApiBase() + '/jobs').then(r => r.ok ? r.json() : []).catch(() => [])
    ])
        .then(([ippData, tcpJobs]) => {
            // 合并 IPP 作业和 TCP 作业
            var ippJobs = Array.isArray(ippData.jobs) ? ippData.jobs : [];
            var allJobs = ippJobs.concat(tcpJobs.map(function(tcpJob) {
                return {
                    id: 'TCP-' + tcpJob.jobId,
                    name: 'TCP 打印任务',
                    queue: 'TCP:' + tcpJob.port,
                    printer: tcpJob.printerName,
                    clientIp: tcpJob.clientIp,
                    user: 'TCP客户端',
                    documentFormat: tcpJob.printMode || 'RAW',
                    sizeText: formatBytes(tcpJob.dataSizeBytes || 0),
                    status: tcpJob.status || 'pending',
                    statusText: tcpJob.status === 'completed' ? '已完成' : tcpJob.status === 'printing' ? '打印中' : tcpJob.status === 'cancelled' ? '已取消' : '等待中',
                    createdAt: tcpJob.createdTime || '',
                    completedAt: tcpJob.completedTime || '',
                    error: tcpJob.errorMessage || '',
                    canStop: tcpJob.status === 'printing',
                    canReprint: false,
                    isTcpJob: true
                };
            }));
            
            jobsCache = allJobs;
            
            // 重新计算统计
            var summary = {
                total: allJobs.length,
                pending: allJobs.filter(j => j.status === 'pending').length,
                printing: allJobs.filter(j => j.status === 'printing').length,
                completed: allJobs.filter(j => j.status === 'completed').length,
                stopped: allJobs.filter(j => j.status === 'stopped' || j.status === 'cancelled').length,
                error: allJobs.filter(j => j.status === 'error').length
            };
            
            updateJobCounters(summary);
            renderJobsTable();
        })
        .catch(err => {
            console.error('加载任务列表失败：', err);
            const tbody = document.getElementById('jobsTableBody');
            if (tbody) {
                tbody.innerHTML = '<tr><td colspan=""8""><div class=""jobs-empty"">加载失败，请稍后重试</div></td></tr>';
            }
        });
}

function formatBytes(bytes) {
    if (bytes === 0) return '0 B';
    var k = 1024;
    var sizes = ['B', 'KB', 'MB', 'GB'];
    var i = Math.floor(Math.log(bytes) / Math.log(k));
    return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}

function updateJobCounters(summary) {
    const counters = Object.assign({ total: 0, pending: 0, printing: 0, completed: 0, stopped: 0, error: 0 }, summary || {});
    const mapping = {
        jobCountAll: counters.total,
        jobCountPending: counters.pending,
        jobCountPrinting: counters.printing,
        jobCountCompleted: counters.completed,
        jobCountStopped: counters.stopped,
        jobCountError: counters.error
    };
    Object.keys(mapping).forEach(id => {
        const el = document.getElementById(id);
        if (el) {
            el.textContent = mapping[id];
        }
    });
}

function renderJobsTable() {
    const tbody = document.getElementById('jobsTableBody');
    if (!tbody) return;
    let filtered = jobsCache;
    if (currentJobTab !== 'all') {
        filtered = jobsCache.filter(job => (job.status || 'pending') === currentJobTab);
    }
    if (!filtered.length) {
        tbody.innerHTML = '<tr><td colspan=""8""><div class=""jobs-empty"">当前没有符合条件的打印任务</div></td></tr>';
        return;
    }
    const rows = filtered.map(job => {
        const queueInfo = job.queue ? '队列：' + escapeHtml(job.queue) : '';
        const formatInfo = job.documentFormat ? '格式：' + escapeHtml(job.documentFormat) : '';
        const clientInfo = job.clientIp ? escapeHtml(job.clientIp) : '未知客户端';
        const jobUri = job.jobUri ? escapeHtml(job.jobUri) : '';
        const sizeInfo = job.sizeText || '-';
        const pages = job.pages && job.pages > 0 ? job.pages : '-';
        const completedAt = job.completedAt ? '<small style=""color:#6b7280"">完成：' + job.completedAt + '</small>' : '';
        // 格式化作业ID：TCP任务显示为#TCP-{id}，IPP任务显示为#IPP-{id}
        var jobIdPrefix = job.isTcpJob ? 'TCP' : 'IPP';
        var formattedJobId = job.isTcpJob ? job.id : (jobIdPrefix + '-' + job.id);
        return '<tr>' +
            '<td><strong>#' + formattedJobId + '</strong></td>' +
            '<td><div>' + escapeHtml(job.name || '(未指定)') + '</div>' +
                (formatInfo ? '<small style=""color:#6b7280"">' + formatInfo + '</small>' : '') +
                (queueInfo ? '<small style=""color:#6b7280; display:block;"">' + queueInfo + '</small>' : '') +
            '</td>' +
            '<td><div>' + escapeHtml(job.user || '未知') + '</div><small style=""color:#6b7280"">' + clientInfo + '</small></td>' +
            '<td><div>' + escapeHtml(job.printer || '-') + '</div>' +
                (jobUri ? '<small style=""color:#6b7280"">' + jobUri + '</small>' : '') +
            '</td>' +
            '<td><div>' + pages + '</div><small style=""color:#6b7280"">' + escapeHtml(sizeInfo) + '</small></td>' +
            '<td>' + renderJobStatusBadge(job) + '</td>' +
            '<td><div>' + (job.createdAt || '-') + '</div>' + completedAt + '</td>' +
            '<td>' + renderJobActions(job) + '</td>' +
            '</tr>';
    }).join('');
    tbody.innerHTML = rows;
}

function renderJobStatusBadge(job) {
    const status = job.status || 'pending';
    const textMap = { pending: '等待中', printing: '打印中', completed: '已完成', error: '错误', stopped: '已停止' };
    const label = job.statusText || textMap[status] || status;
    const tooltip = job.error ? ' title=""' + escapeHtml(job.error) + '""' : '';
    return '<span class=""job-status-badge ' + status + '""' + tooltip + '><span class=""dot""></span>' + label + '</span>';
}

function renderJobActions(job) {
    const jobId = job.id;
    const stopBtn = '<button class=""action-stop"" data-action=""stop-job"" data-job-id=\'' + jobId + '\' ' + (job.canStop ? '' : 'disabled') + '>停止</button>';
    const reprintBtn = '<button class=""action-reprint"" data-action=""reprint-job"" data-job-id=\'' + jobId + '\' ' + (job.canReprint ? '' : 'disabled') + '>重打</button>';
    const deleteBtn = '<button class=""action-delete"" data-action=""delete-job"" data-job-id=\'' + jobId + '\'>删除</button>';
    return '<div class=""job-actions"">' + stopBtn + reprintBtn + deleteBtn + '</div>';
}

function stopJob(jobId) {
    if (!jobId) return;
    if (!confirm('确定要停止作业 #' + jobId + ' 吗？')) {
        return;
    }
    performJobAction(jobId, 'stop');
}

function reprintJob(jobId) {
    if (!jobId) return;
    if (!confirm('确定重打作业 #' + jobId + ' 吗？')) {
        return;
    }
    performJobAction(jobId, 'reprint');
}

function deleteJob(jobId) {
    if (!jobId) return;
    if (!confirm('确定删除作业 #' + jobId + ' 的记录吗？')) {
        return;
    }
    performJobAction(jobId, 'delete');
}

function performJobAction(jobId, action) {
    let url = getJobsApiBase() + '/' + jobId;
    const options = {};
    if (action === 'stop' || action === 'reprint') {
        url += '/' + (action === 'reprint' ? 'reprint' : 'stop');
        options.method = 'POST';
    } else {
        options.method = 'DELETE';
    }
    fetch(url, options)
        .then(response => response.json())
        .then(data => {
            if (data.error || data.success === false) {
                alert(data.message || data.error || '操作失败');
            } else {
                alert(data.message || '操作成功');
                loadJobs();
            }
        })
        .catch(err => {
            console.error('作业操作失败：', err);
            alert('操作失败：' + err.message);
        });
}

initJobTabs();
setTimeout(loadJobs, 500);
setInterval(loadJobs, 10000);

// 加载打印机列表
function loadPrinters() {
    fetch(getPrintersApiBase())
        .then(response => {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(data => {
            const container = document.querySelector('#printers .card');
            if (data.error) {
                container.innerHTML = '<h2>打印机管理</h2><div class=""empty-state""><i>❌</i><p>错误：' + escapeHtml(data.error) + '</p></div>';
                return;
            }
            if (data.printers && data.printers.length > 0) {
                let html = '<h2>打印机管理</h2><div class=""printer-grid"">';
                data.printers.forEach(printer => {
                    const isShared = printer.isShared;
                    const statusClass = printer.status === '在线' ? 'status-completed' : 'status-stopped';
                    html += '<div class=""printer-card ' + (isShared ? 'active' : '') + '"" data-printer=""' + escapeHtml(printer.name) + '"">';
                    html += '<div class=""printer-card-header"" onclick=""togglePrinterDetails(this.closest(\'.printer-card\'))"">';
                    html += '<h3>' + escapeHtml(printer.name) + '</h3>';
                    html += '<div class=""printer-info"">状态: <span class=""status-badge ' + statusClass + '"">' + escapeHtml(printer.status) + '</span></div>';
                    html += '</div>';
                    html += '<div class=""printer-card-actions"">';
                    if (isShared) {
                        html += '<div class=""printer-info"">队列: <strong>' + escapeHtml(printer.queueName) + '</strong></div>';
                        html += '<div class=""printer-uri""><code>' + escapeHtml(printer.uri) + '</code><button class=""copy-btn"" data-action=""copy-uri"" data-uri=""' + escapeHtml(printer.uri) + '"">复制</button></div>';
                        html += '<div style=""margin-top: 10px; display: flex; gap: 5px; flex-wrap: wrap;"">';
                        html += '<button class=""btn btn-sm btn-warning"" data-action=""edit-queue"" data-printer=""' + escapeHtml(printer.name) + '"" data-queue=""' + escapeHtml(printer.queueName) + '"">编辑</button>';
                        html += '<button class=""btn btn-sm btn-danger"" data-action=""remove-queue"" data-printer=""' + escapeHtml(printer.name) + '"">移除</button>';
                        html += '</div>';
                    } else {
                        html += '<div class=""printer-info"">未共享</div>';
                        html += '<button class=""btn btn-sm btn-primary"" style=""margin-top: 10px;"" data-action=""add-queue"" data-printer=""' + escapeHtml(printer.name) + '"">添加共享</button>';
                        }
                    html += '</div>';
                    html += '<div class=""printer-details"" style=""display: none; margin-top: 15px; padding-top: 15px; border-top: 1px solid #eee;"">';
                    html += '<div class=""loading""><div class=""spinner""></div><p>正在加载详细信息...</p></div>';
                    html += '</div>';
                    html += '</div>';
                });
                html += '</div>';
                container.innerHTML = html;
            } else {
                container.innerHTML = '<h2>打印机管理</h2><div class=""empty-state""><i>🖨️</i><p>未检测到任何物理打印机</p></div>';
            }
        })
        .catch(err => {
            console.error('加载打印机列表失败：', err);
            const container = document.querySelector('#printers .card');
            container.innerHTML = '<h2>打印机管理</h2><div class=""empty-state""><i>❌</i><p>加载失败：' + escapeHtml(err.message) + '<br>请刷新页面重试</p></div>';
        });
}

function escapeHtml(text) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

function togglePrinterDetails(card) {
    const details = card.querySelector('.printer-details');
    if (details.style.display === 'none') {
        details.style.display = 'block';
        loadPrinterDetails(card);
    } else {
        details.style.display = 'none';
    }
}

function loadPrinterDetails(card) {
    const printerName = card.getAttribute('data-printer');
    const detailsDiv = card.querySelector('.printer-details');
    // 先获取打印机列表以判断是否共享和服务状态
    fetch(getPrintersApiBase())
        .then(response => response.json())
        .then(printerListData => {
            const printer = printerListData.printers.find(p => p.name === printerName);
            const isShared = printer ? printer.isShared : false;
            const queueName = printer ? printer.queueName : null;
            const isServiceRunning = printerListData.isServiceRunning;
            
            // 获取详细信息
            return fetch(getPrintersApiBase() + '/' + encodeURIComponent(printerName)).then(r => r.json()).then(detailData => {
                return { detailData, isShared, queueName, isServiceRunning };
            });
        })
        .then(({ detailData, isShared, queueName, isServiceRunning }) => {
            let html = '<h4 style=""margin-bottom: 10px;"">详细信息</h4>';
            html += '<div class=""printer-info""><strong>名称：</strong>' + escapeHtml(detailData.name) + '</div>';
            html += '<div class=""printer-info""><strong>状态：</strong>' + escapeHtml(detailData.status) + '</div>';
            html += '<div class=""printer-info""><strong>是否有效：</strong>' + (detailData.isValid ? '是' : '否') + '</div>';
            html += '<div class=""printer-info""><strong>默认打印机：</strong>' + (detailData.isDefault ? '是' : '否') + '</div>';
            html += '<div class=""printer-info""><strong>支持双面打印：</strong>' + (detailData.canDuplex ? '是' : '否') + '</div>';
            html += '<div class=""printer-info""><strong>支持彩色：</strong>' + (detailData.supportsColor ? '是' : '否') + '</div>';
            html += '<div class=""printer-info""><strong>最大份数：</strong>' + (detailData.maximumCopies || 'N/A') + '</div>';
            if (detailData.paperSizes && detailData.paperSizes.length > 0) {
                html += '<div class=""printer-info""><strong>支持的纸张尺寸：</strong></div>';
                html += '<ul style=""margin-left: 20px; margin-top: 5px;"">';
                detailData.paperSizes.slice(0, 10).forEach(ps => {
                    html += '<li>' + escapeHtml(ps.name) + ' (' + ps.width + 'x' + ps.height + ')</li>';
                });
                if (detailData.paperSizes.length > 10) {
                    html += '<li>... 还有 ' + (detailData.paperSizes.length - 10) + ' 种尺寸</li>';
                }
                html += '</ul>';
            }
            
            // 在详细信息区域添加操作按钮
            html += '<div style=""margin-top: 20px; padding-top: 15px; border-top: 2px solid #eee;"">';
            html += '<h4 style=""margin-bottom: 10px;"">队列管理</h4>';
            if (isShared) {
                html += '<div class=""printer-info""><strong>队列名称：</strong>' + escapeHtml(queueName) + '</div>';
                html += '<div style=""margin-top: 10px; display: flex; gap: 5px; flex-wrap: wrap;"">';
                html += '<button class=""btn btn-sm btn-warning"" data-action=""edit-queue"" data-printer=""' + escapeHtml(printerName) + '"" data-queue=""' + escapeHtml(queueName) + '"">编辑队列</button>';
                html += '<button class=""btn btn-sm btn-danger"" data-action=""remove-queue"" data-printer=""' + escapeHtml(printerName) + '"">移除共享</button>';
                html += '</div>';
            } else {
                html += '<div class=""printer-info"">此打印机尚未添加到共享队列</div>';
                html += '<div style=""margin-top: 10px;"">';
                html += '<button class=""btn btn-sm btn-primary"" data-action=""add-queue"" data-printer=""' + escapeHtml(printerName) + '"">添加共享队列</button>';
                html += '</div>';
            }
            html += '</div>';
            
            detailsDiv.innerHTML = html;
        })
        .catch(err => {
            console.error('加载打印机详情失败：', err);
            detailsDiv.innerHTML = '<p style=""color: #dc3545;"">加载详细信息失败</p>';
        });
}

function showAddQueueDialog(printerName) {
    // 创建添加对话框
    const dialog = document.createElement('div');
    dialog.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;';
    const suggestedQueueName = printerName.toLowerCase().replace(/[^a-z0-9]/g, '-');
    dialog.innerHTML = '<div style=""background: white; padding: 20px; border-radius: 8px; min-width: 400px; max-width: 90%;"">' +
        '<h3 style=""margin-top: 0;"">添加共享队列</h3>' +
        '<div class=""form-group"">' +
        '<label>目标打印机：</label>' +
        '<input type=""text"" id=""addPrinterName"" value=""' + escapeHtml(printerName) + '"" readonly style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; background: #f5f5f5;"" />' +
        '</div>' +
        '<div class=""form-group"" style=""margin-top: 15px;"">' +
        '<label>队列名称：</label>' +
        '<input type=""text"" id=""addQueueName"" value=""' + escapeHtml(suggestedQueueName) + '"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"" placeholder=""例如: hp-laserjet"" />' +
        '<small style=""color: #666; display: block; margin-top: 5px;"">队列名称将用于URI路径，只能包含字母、数字和连字符</small>' +
        '</div>' +
        '<div style=""margin-top: 20px; display: flex; gap: 10px; justify-content: flex-end;"">' +
        '<button class=""btn btn-primary"" onclick=""confirmAddQueue();"">添加</button>' +
        '<button class=""btn btn-warning"" onclick=""var p=this.parentElement;while(p&&!p.style.position)p=p.parentElement;if(p)p.remove();"">取消</button>' +
        '</div>' +
        '</div>';
    document.body.appendChild(dialog);
    
    // 聚焦到队列名称输入框并选中文本
    setTimeout(() => {
        const queueInput = document.getElementById('addQueueName');
        if (queueInput) {
            queueInput.focus();
            queueInput.select();
        }
    }, 100);
    
    // 添加回车键支持
    dialog.addEventListener('keydown', function(e) {
        if (e.key === 'Enter' && e.target.id === 'addQueueName') {
            confirmAddQueue();
        } else if (e.key === 'Escape') {
            dialog.remove();
        }
    });
}

function confirmAddQueue() {
    const queueNameInput = document.getElementById('addQueueName');
    const printerNameInput = document.getElementById('addPrinterName');
    if (!queueNameInput) return;
    var dialog = queueNameInput.parentElement;
    while (dialog && dialog.style.position !== 'fixed') dialog = dialog.parentElement;
    
    const queueName = queueNameInput.value.trim();
    const printerName = printerNameInput.value;
    
    if (!queueName) {
        alert('队列名称不能为空');
        return;
    }
    
    if (queueName.includes('/') || queueName.includes('\\')) {
        alert('队列名称不能包含 / 或 \\');
        return;
    }
    
    if (!/^[a-z0-9-]+$/i.test(queueName)) {
        alert('队列名称只能包含字母、数字和连字符');
        return;
    }
    
    addPrinterQueue(printerName, queueName);
    dialog.remove();
}

function addPrinterQueue(printerName, queueName) {
    fetch(getPrintersApiBase(), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ printerName: printerName, queueName: queueName })
    })
        .then(response => response.json())
        .then(data => {
            if (data.error) {
                alert('错误：' + data.error);
            } else {
                alert(data.message || '已成功添加共享队列');
                loadPrinters();
            }
        })
        .catch(err => {
            console.error('添加共享队列失败：', err);
            alert('操作失败，请刷新页面重试');
        });
}

function showEditQueueDialog(printerName, currentQueueName) {
    // 获取所有物理打印机列表
    var apiPath = '/printers/api/printers';
    if (window.location.pathname.indexOf('/printers/') === -1) {
        apiPath = '/api/printers';
    }
    fetch(apiPath)
        .then(response => response.json())
        .then(data => {
            if (data.error) {
                alert('错误：' + data.error);
                return;
            }
            
            // 创建编辑对话框
            const dialog = document.createElement('div');
            dialog.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;';
            dialog.innerHTML = '<div style=""background: white; padding: 20px; border-radius: 8px; min-width: 400px; max-width: 90%;"">' +
                '<h3 style=""margin-top: 0;"">编辑共享队列</h3>' +
                '<div class=""form-group"">' +
                '<label>队列名称：</label>' +
                '<input type=""text"" id=""editQueueName"" value=""' + escapeHtml(currentQueueName) + '"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"" />' +
                '</div>' +
                '<div class=""form-group"" style=""margin-top: 15px;"">' +
                '<label>目标打印机：</label>' +
                '<select id=""editPrinterName"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"">' +
                data.printers.map(p => '<option value=""' + escapeHtml(p.name) + '"" ' + (p.name === printerName ? 'selected' : '') + '>' + escapeHtml(p.name) + '</option>').join('') +
                '</select>' +
                '</div>' +
                '<div style=""margin-top: 20px; display: flex; gap: 10px; justify-content: flex-end;"">' +
                '<button class=""btn btn-primary"" data-action=""confirm-edit-queue"" data-old-printer=""' + escapeHtml(printerName) + '"">保存</button>' +
                '<button class=""btn btn-warning"" onclick=""var p=this.parentElement;while(p&&!p.style.position)p=p.parentElement;if(p)p.remove();"">取消</button>' +
                '</div>' +
                '</div>';
            document.body.appendChild(dialog);
        })
        .catch(err => {
            console.error('加载打印机列表失败：', err);
            alert('加载打印机列表失败，请刷新页面重试');
        });
}

function confirmEditQueue(button) {
    if (!button) return;
    const queueNameInput = document.getElementById('editQueueName');
    const printerNameSelect = document.getElementById('editPrinterName');
    if (!queueNameInput || !printerNameSelect) return;
    
    var dialog = button;
    while (dialog && dialog.style.position !== 'fixed') dialog = dialog.parentElement;
    
    const newQueueName = queueNameInput.value.trim();
    const newPrinterName = printerNameSelect.value;
    
    if (!newQueueName) {
        alert('队列名称不能为空');
        return;
    }
    
    if (newQueueName.includes('/') || newQueueName.includes('\\')) {
        alert('队列名称不能包含 / 或 \\');
        return;
    }
    
    const oldPrinterName = button.getAttribute('data-old-printer') || '';
    updatePrinterQueue(oldPrinterName, newQueueName, newPrinterName);
    dialog.remove();
}

function updatePrinterQueue(oldPrinterName, newQueueName, newPrinterName) {
    const body = {};
    if (newQueueName) body.queueName = newQueueName;
    if (newPrinterName) body.printerName = newPrinterName;
    
    fetch(getPrintersApiBase() + '/' + encodeURIComponent(oldPrinterName), {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body)
    })
        .then(response => response.json())
        .then(data => {
            if (data.error) {
                alert('错误：' + data.error);
            } else {
                alert(data.message || '已成功更新共享队列');
                loadPrinters();
            }
        })
        .catch(err => {
            console.error('更新共享队列失败：', err);
            alert('操作失败，请刷新页面重试');
        });
}

function removePrinterQueue(printerName) {
    if (!confirm('确定要移除打印机 ""' + printerName + '"" 的共享队列吗？')) {
        return;
    }
    fetch(getPrintersApiBase() + '/' + encodeURIComponent(printerName), { method: 'DELETE' })
        .then(response => response.json())
        .then(data => {
            if (data.error) {
                alert('错误：' + data.error);
            } else {
                alert(data.message || '已成功移除共享队列');
                loadPrinters();
            }
        })
        .catch(err => {
            console.error('移除共享队列失败：', err);
            alert('操作失败，请刷新页面重试');
        });
}

function getTcpApiBase() {
    var apiPath = '/printers/api/tcp';
    if (window.location.pathname.indexOf('/printers/') === -1) {
        apiPath = '/api/tcp';
    }
    return apiPath;
}

function loadTcpTasks() {
    var container = document.getElementById('tcp');
    if (!container) return;
    
    container.innerHTML = '<div class=""loading""><div class=""spinner""></div><p>正在加载 TCP 服务配置...</p></div>';
    
    fetch(getTcpApiBase() + '/tasks')
        .then(function(response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }
            return response.json();
        })
        .then(function(data) {
            renderTcpTasks(data, container);
        })
        .catch(function(error) {
            container.innerHTML = '<div class=""error"">加载 TCP 服务配置失败：' + error.message + '</div>';
        });
}

function renderTcpTasks(tasks, container) {
    var html = '<div class=""card""><h2>TCP/IP 打印服务管理</h2>';
    html += '<div style=""margin-bottom: 20px;""><button class=""btn btn-primary"" onclick=""showAddTcpTaskDialog()"">添加共享打印机</button></div>';
    
    if (!tasks || tasks.length === 0) {
        html += '<div class=""empty-state""><i>📭</i><p>当前没有配置 TCP/IP 打印任务</p></div>';
    } else {
        html += '<table class=""queue-table""><thead><tr><th>打印机</th><th>端口</th><th>打印模式</th><th>操作</th></tr></thead><tbody>';
        tasks.forEach(function(task, index) {
            html += '<tr>';
            html += '<td>' + escapeHtml(task.printerName || '') + '</td>';
            html += '<td>' + (task.port || 9100) + '</td>';
            html += '<td>' + escapeHtml(task.printMode || '自动') + '</td>';
            html += '<td>';
            html += '<button class=""copy-btn"" onclick=""showEditTcpTaskDialog(' + index + ', \'' + escapeHtml(task.printerName || '') + '\', ' + (task.port || 9100) + ', \'' + escapeHtml(task.printMode || '自动') + '\')"">编辑</button> ';
            html += '<button class=""copy-btn"" style=""background: #dc3545;"" onclick=""removeTcpTask(' + index + ', \'' + escapeHtml(task.printerName || '') + '\')"">删除</button>';
            html += '</td>';
            html += '</tr>';
        });
        html += '</tbody></table>';
    }
    html += '</div>';
    
    container.innerHTML = html;
}

function showAddTcpTaskDialog() {
    // 获取所有物理打印机列表
    var apiPath = '/printers/api/printers';
    if (window.location.pathname.indexOf('/printers/') === -1) {
        apiPath = '/api/printers';
    }
    fetch(apiPath)
        .then(function(response) { return response.json(); })
        .then(function(data) {
            if (data.error) {
                alert('错误：' + data.error);
                return;
            }
            
            var dialog = document.createElement('div');
            dialog.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;';
            dialog.innerHTML = '<div style=""background: white; padding: 20px; border-radius: 8px; min-width: 400px; max-width: 90%;"">' +
                '<h3 style=""margin-top: 0;"">添加 TCP/IP 打印任务</h3>' +
                '<div class=""form-group"">' +
                '<label>打印机：</label>' +
                '<select id=""addTcpPrinterName"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"">' +
                data.printers.map(p => '<option value=""' + escapeHtml(p.name) + '"">' + escapeHtml(p.name) + '</option>').join('') +
                '</select>' +
                '</div>' +
                '<div class=""form-group"" style=""margin-top: 15px;"">' +
                '<label>端口：</label>' +
                '<input type=""number"" id=""addTcpPort"" value=""9100"" min=""1"" max=""65535"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"" />' +
                '</div>' +
                '<div class=""form-group"" style=""margin-top: 15px;"">' +
                '<label>打印模式：</label>' +
                '<select id=""addTcpPrintMode"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"">' +
                '<option value=""自动"">自动</option>' +
                '<option value=""RAW"">RAW</option>' +
                '<option value=""XPS"">XPS</option>' +
                '</select>' +
                '</div>' +
                '<div style=""margin-top: 20px; display: flex; gap: 10px; justify-content: flex-end;"">' +
                '<button class=""btn btn-primary"" onclick=""confirmAddTcpTask()"">添加</button>' +
                '<button class=""btn btn-warning"" onclick=""this.parentElement.parentElement.parentElement.remove();"">取消</button>' +
                '</div>' +
                '</div>';
            document.body.appendChild(dialog);
        })
        .catch(function(err) {
            alert('加载打印机列表失败：' + err.message);
        });
}

function confirmAddTcpTask() {
    var printerName = document.getElementById('addTcpPrinterName').value;
    var port = parseInt(document.getElementById('addTcpPort').value) || 9100;
    var printMode = document.getElementById('addTcpPrintMode').value || '自动';
    
    if (!printerName) {
        alert('请选择打印机');
        return;
    }
    
    if (port < 1 || port > 65535) {
        alert('端口范围必须在 1 - 65535 之间');
        return;
    }
    
    fetch(getTcpApiBase() + '/tasks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ printerName: printerName, port: port, printMode: printMode })
    })
    .then(function(response) { return response.json(); })
    .then(function(data) {
        if (data.error) {
            alert('错误：' + data.error);
        } else {
            alert(data.message || '已成功添加 TCP 任务');
            loadTcpTasks();
        }
        var dialog = document.querySelector('div[style*=""position: fixed""]');
        if (dialog) dialog.remove();
    })
    .catch(function(err) {
        alert('添加 TCP 任务失败：' + err.message);
    });
}

function showEditTcpTaskDialog(index, printerName, port, printMode) {
    var apiPath = '/printers/api/printers';
    if (window.location.pathname.indexOf('/printers/') === -1) {
        apiPath = '/api/printers';
    }
    fetch(apiPath)
        .then(function(response) { return response.json(); })
        .then(function(data) {
            if (data.error) {
                alert('错误：' + data.error);
                return;
            }
            
            var dialog = document.createElement('div');
            dialog.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;';
            dialog.innerHTML = '<div style=""background: white; padding: 20px; border-radius: 8px; min-width: 400px; max-width: 90%;"">' +
                '<h3 style=""margin-top: 0;"">编辑 TCP/IP 打印任务</h3>' +
                '<div class=""form-group"">' +
                '<label>打印机：</label>' +
                '<select id=""editTcpPrinterName"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"">' +
                data.printers.map(p => '<option value=""' + escapeHtml(p.name) + '"" ' + (p.name === printerName ? 'selected' : '') + '>' + escapeHtml(p.name) + '</option>').join('') +
                '</select>' +
                '</div>' +
                '<div class=""form-group"" style=""margin-top: 15px;"">' +
                '<label>端口：</label>' +
                '<input type=""number"" id=""editTcpPort"" value=""' + port + '"" min=""1"" max=""65535"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"" />' +
                '</div>' +
                '<div class=""form-group"" style=""margin-top: 15px;"">' +
                '<label>打印模式：</label>' +
                '<select id=""editTcpPrintMode"" style=""width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"">' +
                '<option value=""自动"" ' + (printMode === '自动' ? 'selected' : '') + '>自动</option>' +
                '<option value=""RAW"" ' + (printMode === 'RAW' ? 'selected' : '') + '>RAW</option>' +
                '<option value=""XPS"" ' + (printMode === 'XPS' ? 'selected' : '') + '>XPS</option>' +
                '</select>' +
                '</div>' +
                '<div style=""margin-top: 20px; display: flex; gap: 10px; justify-content: flex-end;"">' +
                '<button class=""btn btn-primary"" onclick=""confirmEditTcpTask(' + index + ')"">保存</button>' +
                '<button class=""btn btn-warning"" onclick=""this.parentElement.parentElement.parentElement.remove();"">取消</button>' +
                '</div>' +
                '</div>';
            document.body.appendChild(dialog);
        })
        .catch(function(err) {
            alert('加载打印机列表失败：' + err.message);
        });
}

function confirmEditTcpTask(index) {
    var printerName = document.getElementById('editTcpPrinterName').value;
    var port = parseInt(document.getElementById('editTcpPort').value) || 9100;
    var printMode = document.getElementById('editTcpPrintMode').value || '自动';
    
    if (!printerName) {
        alert('请选择打印机');
        return;
    }
    
    if (port < 1 || port > 65535) {
        alert('端口范围必须在 1 - 65535 之间');
        return;
    }
    
    fetch(getTcpApiBase() + '/tasks/' + index, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ printerName: printerName, port: port, printMode: printMode })
    })
    .then(function(response) { return response.json(); })
    .then(function(data) {
        if (data.error) {
            alert('错误：' + data.error);
        } else {
            alert(data.message || '已成功更新 TCP 任务');
            loadTcpTasks();
        }
        var dialog = document.querySelector('div[style*=""position: fixed""]');
        if (dialog) dialog.remove();
    })
    .catch(function(err) {
        alert('更新 TCP 任务失败：' + err.message);
    });
}

function removeTcpTask(index, printerName) {
    if (!confirm('确定要删除 TCP 任务：' + printerName + ' 吗？')) {
        return;
    }
    fetch(getTcpApiBase() + '/tasks/' + index, { method: 'DELETE' })
        .then(function(response) { return response.json(); })
        .then(function(data) {
            if (data.error) {
                alert('错误：' + data.error);
            } else {
                alert(data.message || '已成功删除 TCP 任务');
                loadTcpTasks();
            }
        })
        .catch(function(err) {
            alert('删除 TCP 任务失败：' + err.message);
        });
}

function controlService(action) {
    if (!action) {
        return;
    }
    
    var statusMsg = document.getElementById('serviceStatusMessage');
    if (statusMsg) {
        statusMsg.textContent = '正在执行 ' + action + ' 操作...';
    }
    
    document.querySelectorAll('#tab-status .btn-group button').forEach(btn => btn.disabled = true);
    
    fetch(getSystemApiBase() + '/service/' + action, {
        method: 'POST'
    })
    .then(function(response) {
        if (!response.ok) {
            return response.json().then(function(err) {
                throw new Error(err.message || ('HTTP ' + response.status));
            });
        }
        return response.json();
    })
    .then(function(result) {
        if (statusMsg) {
            statusMsg.textContent = result.message || '';
        }
        if (!result.success) {
            alert('操作失败：' + (result.message || '未知错误'));
        }
        loadSystemLogs();
    })
    .catch(function(error) {
        console.error('控制服务失败：', error);
        if (statusMsg) {
            statusMsg.textContent = '操作失败：' + error.message;
        }
        alert('操作失败：' + error.message);
    })
    .finally(function() {
        loadServiceStatus();
    });
}

// 页面加载时加载服务状态
loadServiceStatus();

// 页面加载时加载打印机列表
setTimeout(loadPrinters, 500);
setInterval(loadPrinters, 10000); // 每10秒刷新一次
" + LoadRestrictionJs() + @"
</script>";
        }

        #region API处理方法

        public async Task HandleApiRequestAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            var absolutePath = request.Url.AbsolutePath.TrimStart('/');
            var path = absolutePath.ToLower();
            
            // 动态移除路径前缀（如 /printers/, /ipp/ 等）
            // 匹配格式：{prefix}/api/{endpoint}
            var apiMatch = System.Text.RegularExpressions.Regex.Match(path, @"^([^/]+)/api/(.+)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            if (apiMatch.Success)
            {
                // 提取API路径部分（去掉前缀）
                path = "api/" + apiMatch.Groups[2].Value;
            }
            // 如果没有匹配到前缀，直接使用原路径（可能是 /api/... 格式）
            
            // 登录API不需要认证
            if (path == "api/auth/login" || path.StartsWith("api/auth/login"))
            {
                await HandleLoginApiAsync(request, response, token).ConfigureAwait(false);
                return;
            }
            
            // 登出API需要认证
            if (path == "api/auth/logout" || path.StartsWith("api/auth/logout"))
            {
                await HandleLogoutApiAsync(request, response, token).ConfigureAwait(false);
                return;
            }
            
            // 其他API需要认证检查
            if (!CheckAuthentication(request, response))
            {
                return; // CheckAuthentication已经发送了响应
            }
            
            if (path == "api/jobs" || path.StartsWith("api/jobs/"))
            {
                await HandleJobsApiAsync(request, response, token).ConfigureAwait(false);
                return;
            }
            
            if (path == "api/system" || path.StartsWith("api/system/"))
            {
                await HandleSystemApiAsync(request, response, token).ConfigureAwait(false);
                return;
            }
            
            if (path == "api/printers" || path.StartsWith("api/printers/"))
            {
                await HandlePrintersApiAsync(request, response, token).ConfigureAwait(false);
                return;
            }
            
            if (path == "api/tcp" || path.StartsWith("api/tcp/"))
            {
                await HandleTcpApiAsync(request, response, token).ConfigureAwait(false);
                return;
            }
            
            var error = new Dictionary<string, object> { ["error"] = "API endpoint not found" };
            await WriteJsonResponse(response, HttpStatusCode.NotFound, error).ConfigureAwait(false);
        }

        /// <summary>
        /// 检查认证状态
        /// </summary>
        private bool CheckAuthentication(HttpListenerRequest request, HttpListenerResponse response)
        {
            var cookieHeader = request.Headers["Cookie"];
            var sessionId = _authManager.GetSessionIdFromCookie(cookieHeader);
            
            if (!_authManager.ValidateSession(sessionId))
            {
                var error = new Dictionary<string, object> 
                { 
                    ["error"] = "Unauthorized",
                    ["message"] = "需要登录",
                    ["requiresAuth"] = true
                };
                WriteJsonResponse(response, HttpStatusCode.Unauthorized, error).Wait();
                return false;
            }
            
            return true;
        }

        /// <summary>
        /// 处理登录API
        /// </summary>
        private async Task HandleLoginApiAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            try
            {
                var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                var bodyText = Encoding.UTF8.GetString(body);
                var data = ParseJsonToDictionary(bodyText);
                var password = data.ContainsKey("password") ? data["password"]?.ToString() : "";

                if (string.IsNullOrEmpty(password))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "密码不能为空" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }

                // 加载配置获取密码哈希
                var settings = Configuration.AppSettingsStore.Load();
                var passwordHash = settings.AdminPasswordHash;

                // WebUI不允许首次登录（如果密码为空，拒绝登录）
                // 首次密码必须通过GUI设置
                if (string.IsNullOrEmpty(passwordHash))
                {
                    var error = new Dictionary<string, object> 
                    { 
                        ["success"] = false, 
                        ["message"] = "未设置管理员密码。请通过GUI界面首次设置密码。",
                        ["requiresGuiInit"] = true
                    };
                    await WriteJsonResponse(response, HttpStatusCode.Forbidden, error).ConfigureAwait(false);
                    return;
                }

                // 创建Session（WebUI不允许首次登录）
                var sessionId = _authManager.CreateSession(passwordHash, password, allowFirstTimeLogin: false);
                
                if (sessionId == null)
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "密码错误" };
                    await WriteJsonResponse(response, HttpStatusCode.Unauthorized, error).ConfigureAwait(false);
                    return;
                }

                // 设置Cookie
                response.Headers.Add("Set-Cookie", $"ipp_admin_session={sessionId}; Path=/; HttpOnly; Max-Age={30 * 60}"); // 30分钟

                var result = new Dictionary<string, object> 
                { 
                    ["success"] = true,
                    ["message"] = string.IsNullOrEmpty(passwordHash) ? "首次登录成功，请在系统管理中设置新密码" : "登录成功"
                };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// 处理登出API
        /// </summary>
        private async Task HandleLogoutApiAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            try
            {
                var cookieHeader = request.Headers["Cookie"];
                var sessionId = _authManager.GetSessionIdFromCookie(cookieHeader);
                
                if (!string.IsNullOrEmpty(sessionId))
                {
                    _authManager.RemoveSession(sessionId);
                }

                // 清除Cookie
                response.Headers.Add("Set-Cookie", "ipp_admin_session=; Path=/; HttpOnly; Max-Age=0");

                var result = new Dictionary<string, object> { ["success"] = true, ["message"] = "已登出" };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private async Task HandleJobsApiAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            var absolutePath = request.Url.AbsolutePath.TrimStart('/');
            var normalizedPath = absolutePath;
            // 动态移除路径前缀（如 /printers/, /ipp/ 等）
            var apiMatch = System.Text.RegularExpressions.Regex.Match(normalizedPath, @"^([^/]+)/api/(.+)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            if (apiMatch.Success)
            {
                normalizedPath = "api/" + apiMatch.Groups[2].Value;
            }
            
            if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase) &&
                (string.Equals(normalizedPath, "api/jobs", StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(normalizedPath, "api/jobs/", StringComparison.OrdinalIgnoreCase)))
            {
                var statusFilter = request.QueryString["status"];
                await HandleJobsListAsync(response, statusFilter).ConfigureAwait(false);
                return;
            }
            
            if (normalizedPath.StartsWith("api/jobs/", StringComparison.OrdinalIgnoreCase))
            {
                var remainder = normalizedPath.Substring("api/jobs/".Length);
                var segments = remainder.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                if (segments.Length == 0 || !int.TryParse(segments[0], out var jobId))
                {
                    var invalidId = new Dictionary<string, object> { ["error"] = "无效的作业ID" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, invalidId).ConfigureAwait(false);
                    return;
                }
                
                if (string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    if (segments.Length < 2)
                    {
                        var error = new Dictionary<string, object> { ["error"] = "未指定操作类型" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                        return;
                    }
                    
                    var action = segments[1];
                    (bool Success, string Message) result;
                    
                    if (string.Equals(action, "stop", StringComparison.OrdinalIgnoreCase))
                    {
                        result = await _stopJobCallback(jobId).ConfigureAwait(false);
                    }
                    else if (string.Equals(action, "reprint", StringComparison.OrdinalIgnoreCase) ||
                             string.Equals(action, "replay", StringComparison.OrdinalIgnoreCase))
                    {
                        result = await _replayJobCallback(jobId).ConfigureAwait(false);
                    }
                    else
                    {
                        var unknown = new Dictionary<string, object> { ["error"] = $"未知的作业操作：{action}" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, unknown).ConfigureAwait(false);
                        return;
                    }
                    
                    var payload = new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    };
                    await WriteJsonResponse(response, result.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest, payload).ConfigureAwait(false);
                    return;
                }
                
                if (string.Equals(request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase))
                {
                    var result = await _deleteJobCallback(jobId).ConfigureAwait(false);
                    var payload = new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    };
                    await WriteJsonResponse(response, result.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest, payload).ConfigureAwait(false);
                    return;
                }
            }
            
            var methodError = new Dictionary<string, object> { ["error"] = "不支持的请求" };
            await WriteJsonResponse(response, HttpStatusCode.MethodNotAllowed, methodError).ConfigureAwait(false);
        }

        private async Task HandleJobsListAsync(HttpListenerResponse response, string statusFilter)
        {
            var snapshots = new List<(DateTime createdAt, Dictionary<string, object> data)>();
            var now = DateTime.Now;
            
            foreach (var kvp in _completedJobs)
            {
                var queueName = kvp.Key;
                var jobList = kvp.Value;
                lock (jobList)
                {
                    CleanupCompletedJobs(jobList, now);
                    foreach (var job in jobList)
                    {
                        var status = MapJobStatus(job.JobState);
                        var dto = new Dictionary<string, object>
                        {
                            ["id"] = job.JobId,
                            ["isTcpJob"] = false, // IPP任务标记
                            ["name"] = job.JobName ?? "(未指定)",
                            ["printer"] = job.PrinterName ?? "未知",
                            ["queue"] = queueName,
                            ["user"] = job.UserName ?? "未知用户",
                            ["clientIp"] = job.ClientIp ?? "未知",
                            ["status"] = status,
                            ["statusText"] = GetJobStateText(job.JobState),
                            ["stateCode"] = job.JobState,
                            ["createdAt"] = job.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"),
                            ["completedAt"] = job.CompletedTime != DateTime.MaxValue ? job.CompletedTime.ToString("yyyy-MM-dd HH:mm:ss") : null,
                            ["sizeBytes"] = job.DocumentSizeBytes,
                            ["sizeText"] = FormatFileSize(job.DocumentSizeBytes),
                            ["documentFormat"] = job.DocumentFormat ?? "未知",
                            ["pages"] = job.PageCount > 0 ? job.PageCount : 0,
                            ["jobUri"] = job.JobUri ?? string.Empty,
                            ["error"] = string.IsNullOrWhiteSpace(job.ErrorMessage) ? null : job.ErrorMessage,
                            ["canStop"] = CanStopJob(job),
                            ["canReprint"] = CanReplayJob(job),
                            ["canDelete"] = true
                        };
                        snapshots.Add((job.CreatedTime, dto));
                    }
                }
            }
            
            var ordered = snapshots
                .OrderByDescending(s => s.createdAt)
                .Select(s => s.data)
                .ToList();
            
            var filtered = ordered;
            if (!string.IsNullOrEmpty(statusFilter) && !"all".Equals(statusFilter, StringComparison.OrdinalIgnoreCase))
            {
                filtered = ordered
                    .Where(j => string.Equals(j["status"] as string, statusFilter, StringComparison.OrdinalIgnoreCase))
                    .ToList();
            }
            
            var statusCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            foreach (var snapshot in snapshots)
            {
                var status = snapshot.data["status"] as string ?? "pending";
                if (!statusCounts.ContainsKey(status))
                {
                    statusCounts[status] = 0;
                }
                statusCounts[status]++;
            }
            
            int GetCount(string key) => statusCounts.TryGetValue(key, out var value) ? value : 0;
            
            var summary = new Dictionary<string, object>
            {
                ["total"] = snapshots.Count,
                ["pending"] = GetCount("pending"),
                ["printing"] = GetCount("printing"),
                ["completed"] = GetCount("completed"),
                ["stopped"] = GetCount("stopped"),
                ["error"] = GetCount("error")
            };
            
            var result = new Dictionary<string, object>
            {
                ["jobs"] = filtered,
                ["summary"] = summary
            };
            
            await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
        }

        private async Task HandleSystemApiAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            var absolutePath = request.Url.AbsolutePath.TrimStart('/');
            var path = absolutePath.ToLower();
            
            // 动态移除路径前缀（如 /printers/, /ipp/ 等）
            string remainingPath = null;
            var apiMatch = System.Text.RegularExpressions.Regex.Match(path, @"^[^/]+/api/system/(.+)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            if (apiMatch.Success)
            {
                remainingPath = apiMatch.Groups[1].Value;
            }
            else if (path.StartsWith("api/system/", StringComparison.OrdinalIgnoreCase))
            {
                remainingPath = absolutePath.Substring("api/system/".Length);
            }
            
            if (string.IsNullOrEmpty(remainingPath))
            {
                remainingPath = "status";
            }
            
            remainingPath = remainingPath.ToLower();
            
            if (remainingPath == "status")
            {
                // 线程安全地获取当前配置
                ServerConfig currentConfig;
                lock (_configLock)
                {
                    currentConfig = _config;
                }
                
                var localIps = _getLocalIpAddresses();
                var port = _extractPortFromPrefix(currentConfig.BasePrefix);
                
                var settings = Configuration.AppSettingsStore.Load();
                var result = new Dictionary<string, object>
                {
                    ["isRunning"] = _listener.IsListening,
                    ["ippServiceRunning"] = _getIppServiceStateCallback?.Invoke() ?? true,
                    ["tcpServiceRunning"] = _getTcpServiceStateCallback?.Invoke() ?? false,
                    ["installedPrinters"] = System.Drawing.Printing.PrinterSettings.InstalledPrinters.Count,
                    ["sharedQueues"] = currentConfig.QueueToPrinter.Count,
                    ["baseUrl"] = localIps.Count > 0 ? $"http://{localIps[0]}:{port}" : currentConfig.BasePrefix.TrimEnd('/'),
                    ["enableMdns"] = _enableMdnsDiscovery,
                    ["enableTcp9100Service"] = settings.EnableTcp9100Service
                };
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
                return;
            }
            
            if (remainingPath == "settings")
            {
                if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleGetSystemSettingsAsync(response).ConfigureAwait(false);
                    return;
                }
                if (string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleUpdateSystemSettingsAsync(request, response, token).ConfigureAwait(false);
                    return;
                }
            }
            
            if (remainingPath == "logs")
            {
                if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleGetSystemLogsAsync(response).ConfigureAwait(false);
                    return;
                }
                if (string.Equals(request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleClearSystemLogsAsync(response).ConfigureAwait(false);
                    return;
                }
            }
            
            if (remainingPath == "restriction" || remainingPath.StartsWith("restriction/", StringComparison.OrdinalIgnoreCase))
            {
                await HandleRestrictionApiAsync(request, response, remainingPath, token).ConfigureAwait(false);
                return;
            }
            
            if (remainingPath == "logs/export")
            {
                if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleExportSystemLogsAsync(response).ConfigureAwait(false);
                    return;
                }
            }

            // TCP 服务控制
            if (remainingPath == "tcp/start" || remainingPath == "tcp/stop")
            {
                if (!string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    var methodError = new Dictionary<string, object> { ["success"] = false, ["message"] = "仅支持 POST 请求" };
                    await WriteJsonResponse(response, HttpStatusCode.MethodNotAllowed, methodError).ConfigureAwait(false);
                    return;
                }
                
                if (remainingPath == "tcp/start")
                {
                    var result = _startTcpServiceCallback?.Invoke() ?? (false, "TCP 服务回调未设置");
                    await WriteJsonResponse(response, HttpStatusCode.OK, new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    }).ConfigureAwait(false);
                    return;
                }
                
                if (remainingPath == "tcp/stop")
                {
                    var result = _stopTcpServiceCallback?.Invoke() ?? (false, "TCP 服务回调未设置");
                    await WriteJsonResponse(response, HttpStatusCode.OK, new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    }).ConfigureAwait(false);
                    return;
                }
            }
            
            if (remainingPath.StartsWith("service", StringComparison.OrdinalIgnoreCase))
            {
                if (!string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    var methodError = new Dictionary<string, object> { ["success"] = false, ["message"] = "仅支持 POST 请求" };
                    await WriteJsonResponse(response, HttpStatusCode.MethodNotAllowed, methodError).ConfigureAwait(false);
                    return;
                }

                var actionPart = remainingPath.Length > "service".Length
                    ? remainingPath.Substring("service".Length).TrimStart('/')
                    : string.Empty;

                (bool Success, string Message) opResult;
                switch (actionPart)
                {
                    case "start":
                        opResult = _startIppServiceCallback != null
                            ? _startIppServiceCallback()
                            : (false, "该操作不可用");
                        break;
                    case "stop":
                        opResult = _stopIppServiceCallback != null
                            ? _stopIppServiceCallback()
                            : (false, "该操作不可用");
                        break;
                    case "restart":
                        opResult = _restartIppServiceCallback != null
                            ? _restartIppServiceCallback()
                            : (false, "该操作不可用");
                        break;
                    default:
                        var unknownAction = new Dictionary<string, object> { ["success"] = false, ["message"] = "未知的服务操作" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, unknownAction).ConfigureAwait(false);
                        return;
                }

                var payload = new Dictionary<string, object>
                {
                    ["success"] = opResult.Success,
                    ["message"] = opResult.Message
                };
                await WriteJsonResponse(response, opResult.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest, payload).ConfigureAwait(false);
                return;
            }
            
            var error = new Dictionary<string, object> { ["error"] = "API endpoint not found" };
            await WriteJsonResponse(response, HttpStatusCode.NotFound, error).ConfigureAwait(false);
        }

        private async Task HandleGetSystemSettingsAsync(HttpListenerResponse response)
        {
            try
            {
                var settings = Configuration.AppSettingsStore.Load();
                var localIps = _getLocalIpAddresses();
                
                // 从BasePrefix中提取参数
                var basePrefix = settings.BasePrefix ?? "http://+:8631/printers/";
                var port = _extractPortFromPrefix(basePrefix);
                var path = _extractPathFromPrefix(basePrefix);
                
                // 提取监听网卡
                string selectedInterface = "+";
                try
                {
                    var uri = new Uri(basePrefix);
                    var host = uri.Host;
                    if (host != "+" && host != "*" && host != "0.0.0.0")
                    {
                        selectedInterface = host;
                    }
                }
                catch
                {
                    // 使用默认值
                }
                
                // 获取网络接口列表
                var interfaces = new List<Dictionary<string, object>>
                {
                    new Dictionary<string, object> { ["value"] = "+", ["label"] = "所有网卡 (0.0.0.0)" }
                };
                
                try
                {
                    foreach (var ni in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
                    {
                        if (ni.OperationalStatus != System.Net.NetworkInformation.OperationalStatus.Up)
                        {
                            continue;
                        }
                        if (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Loopback ||
                            ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Tunnel)
                        {
                            continue;
                        }
                        
                        var ipProps = ni.GetIPProperties();
                        foreach (var unicast in ipProps.UnicastAddresses)
                        {
                            var address = unicast.Address;
                            if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork &&
                                !System.Net.IPAddress.IsLoopback(address))
                            {
                                var ipText = address.ToString();
                                if (!ipText.StartsWith("169.254.", StringComparison.Ordinal))
                                {
                                    var label = $"{ipText} ({ni.Name})";
                                    interfaces.Add(new Dictionary<string, object>
                                    {
                                        ["value"] = ipText,
                                        ["label"] = label
                                    });
                                    break; // 每个接口只取第一个IP
                                }
                            }
                        }
                    }
                }
                catch
                {
                    // 忽略错误
                }
                
                var result = new Dictionary<string, object>
                {
                    ["interface"] = selectedInterface,
                    ["port"] = port,
                    ["path"] = path,
                    ["autoStart"] = settings.AutoStart,
                    ["runOnBoot"] = settings.RunOnBoot,
                    ["enableMdns"] = settings.EnableMdnsDiscovery,
                    ["enableTcp9100Service"] = settings.EnableTcp9100Service,
                    ["interfaces"] = interfaces,
                    ["hasAdminPassword"] = !string.IsNullOrEmpty(settings.AdminPasswordHash)
                };
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private async Task HandleUpdateSystemSettingsAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            try
            {
                var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                var bodyText = Encoding.UTF8.GetString(body);
                
                // 简单的JSON解析（.NET 4.6.2兼容）
                var settings = Configuration.AppSettingsStore.Load();
                
                // 解析JSON
                var data = ParseJsonToDictionary(bodyText);
                
                var interfaceValue = data.ContainsKey("interface") ? data["interface"]?.ToString() : "+";
                var port = data.ContainsKey("port") && int.TryParse(data["port"]?.ToString(), out var p) ? p : 8631;
                var path = data.ContainsKey("path") ? data["path"]?.ToString() : "/printers/";
                var autoStart = data.ContainsKey("autoStart") && bool.TryParse(data["autoStart"]?.ToString(), out var asVal) ? asVal : false;
                var runOnBoot = data.ContainsKey("runOnBoot") && bool.TryParse(data["runOnBoot"]?.ToString(), out var robVal) ? robVal : false;
                var enableMdns = data.ContainsKey("enableMdns") && bool.TryParse(data["enableMdns"]?.ToString(), out var emVal) ? emVal : false;
                var enableTcp9100Service = data.ContainsKey("enableTcp9100Service") && bool.TryParse(data["enableTcp9100Service"]?.ToString(), out var etVal) ? etVal : false;
                
                // 处理密码修改
                if (data.ContainsKey("adminPassword") && !string.IsNullOrEmpty(data["adminPassword"]?.ToString()))
                {
                    var newPassword = data["adminPassword"]?.ToString();
                    if (newPassword.Length < 6)
                    {
                        var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "密码长度至少为6个字符" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                        return;
                    }
                    
                    // 检查是否允许设置密码
                    // 如果当前没有密码，且没有初始化令牌，则不允许通过WebUI设置
                    var currentPasswordHash = settings.AdminPasswordHash;
                    if (string.IsNullOrEmpty(currentPasswordHash))
                    {
                        if (!Configuration.AdminPasswordInitializer.IsInitialized())
                        {
                            var error = new Dictionary<string, object> 
                            { 
                                ["success"] = false, 
                                ["message"] = "首次设置密码必须通过GUI界面完成。请使用GUI界面设置初始密码。",
                                ["requiresGuiInit"] = true
                            };
                            await WriteJsonResponse(response, HttpStatusCode.Forbidden, error).ConfigureAwait(false);
                            return;
                        }
                        // 有初始化令牌，允许设置密码，设置后删除令牌
                        settings.AdminPasswordHash = AuthManager.HashPassword(newPassword);
                        Configuration.AdminPasswordInitializer.RemoveInitToken();
                    }
                    else
                    {
                        // 修改已有密码
                        settings.AdminPasswordHash = AuthManager.HashPassword(newPassword);
                    }
                }
                
                // 验证
                if (port < 1 || port > 65535)
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "端口范围必须在 1 - 65535 之间" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                if (!path.StartsWith("/") || !path.EndsWith("/"))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "访问路径必须以 / 开头和结尾" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 构建BasePrefix
                var hostSegment = interfaceValue == "+" ? "+" : interfaceValue;
                var newBasePrefix = $"http://{hostSegment}:{port}{path}";
                
                // 更新设置
                settings.BasePrefix = newBasePrefix;
                settings.AutoStart = autoStart;
                settings.RunOnBoot = runOnBoot;
                settings.EnableMdnsDiscovery = enableMdns;
                settings.EnableTcp9100Service = enableTcp9100Service;
                settings.LastUsedPort = port;
                
                // 保存设置
                Configuration.AppSettingsStore.Save(settings);
                
                // 获取新的baseUrl用于前端刷新
                var localIps = _getLocalIpAddresses();
                var newBaseUrl = localIps.Count > 0 
                    ? $"http://{localIps[0]}:{port}{path}" 
                    : newBasePrefix.TrimEnd('/');
                
                var result = new Dictionary<string, object>
                {
                    ["success"] = true,
                    ["message"] = "设置已保存并已同步到GUI界面，配置已重载",
                    ["newBaseUrl"] = newBaseUrl,
                    ["newPort"] = port,
                    ["newPath"] = path
                };
                
                // 先发送响应，确保前端能收到
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
                
                // 确保响应数据已完全发送到客户端
                try
                {
                    response.OutputStream.Flush();
                }
                catch
                {
                    // 忽略刷新时的错误（可能已经关闭）
                }
                
                // 延迟触发配置重载，避免中断当前请求
                // 在后台任务中延迟执行，给响应足够时间发送到客户端
                _ = Task.Run(async () =>
                {
                    try
                    {
                        // 延迟 1 秒，确保响应已完全发送到客户端
                        await Task.Delay(1000).ConfigureAwait(false);
                        
                        // 通知GUI界面重新加载配置（通过FileSystemWatcher会自动检测）
                        // 同时调用配置重载回调以更新运行中的服务配置
                        if (_reloadConfigCallback != null)
                        {
                            try
                            {
                                var newConfig = ServerConfig.FromUserInput(newBasePrefix, settings.PrinterMappings);
                                // 更新当前配置（热更新）
                                lock (_configLock)
                                {
                                    _config = newConfig;
                                }
                                // 触发配置重载回调（会调用IppServerHost.ReloadConfig()）
                                _reloadConfigCallback();
                            }
                            catch (Exception ex)
                            {
                                _logCallback?.Invoke($"重载配置失败：{ex.Message}");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logCallback?.Invoke($"延迟重载配置时发生错误：{ex.Message}");
                    }
                });
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private Dictionary<string, object> ParseJsonToDictionary(string json)
        {
            var result = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            if (string.IsNullOrWhiteSpace(json))
            {
                return result;
            }
            
            try
            {
                // 使用DataContractJsonSerializer解析JSON
                using (var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(json)))
                {
                    var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(
                        typeof(Dictionary<string, object>),
                        new System.Runtime.Serialization.Json.DataContractJsonSerializerSettings 
                        { 
                            UseSimpleDictionaryFormat = true 
                        });
                    var parsed = serializer.ReadObject(stream) as Dictionary<string, object>;
                    if (parsed != null)
                    {
                        return parsed;
                    }
                }
            }
            catch
            {
                // 如果解析失败，使用简单方法
                json = json.Trim();
                if (json.StartsWith("{") && json.EndsWith("}"))
                {
                    json = json.Substring(1, json.Length - 2);
                    var pairs = json.Split(',');
                    foreach (var pair in pairs)
                    {
                        var parts = pair.Split(new[] { ':' }, 2);
                        if (parts.Length == 2)
                        {
                            var key = parts[0].Trim().Trim('"', '\'', ' ');
                            var value = parts[1].Trim().Trim('"', '\'', ' ');
                            
                            // 尝试解析为布尔值
                            if (value.Equals("true", StringComparison.OrdinalIgnoreCase))
                            {
                                result[key] = true;
                            }
                            else if (value.Equals("false", StringComparison.OrdinalIgnoreCase))
                            {
                                result[key] = false;
                            }
                            else if (int.TryParse(value, out var intVal))
                            {
                                result[key] = intVal;
                            }
                            else
                            {
                                result[key] = value;
                            }
                        }
                    }
                }
            }
            
            return result;
        }

        private async Task HandleGetSystemLogsAsync(HttpListenerResponse response)
        {
            try
            {
                var logs = new List<Dictionary<string, object>>();
                
                if (_getLogsCallback != null)
                {
                    var logEntries = _getLogsCallback();
                    foreach (var logEntry in logEntries)
                    {
                        logs.Add(new Dictionary<string, object>
                        {
                            ["timestamp"] = logEntry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
                            ["message"] = logEntry.Message
                        });
                    }
                }
                
                var result = new Dictionary<string, object>
                {
                    ["logs"] = logs
                };
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private async Task HandleClearSystemLogsAsync(HttpListenerResponse response)
        {
            try
            {
                if (_clearLogsCallback != null)
                {
                    _clearLogsCallback();
                }
                
                var result = new Dictionary<string, object>
                {
                    ["success"] = true,
                    ["message"] = "日志已清除"
                };
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private async Task HandleExportSystemLogsAsync(HttpListenerResponse response)
        {
            try
            {
                string logText = "暂无日志记录";
                if (_exportLogsCallback != null)
                {
                    logText = _exportLogsCallback();
                    if (string.IsNullOrEmpty(logText))
                    {
                        logText = "暂无日志记录";
                    }
                }
                
                var content = Encoding.UTF8.GetBytes(logText);
                response.StatusCode = (int)HttpStatusCode.OK;
                response.ContentType = "text/plain; charset=utf-8";
                response.ContentLength64 = content.Length;
                response.Headers.Add("Content-Disposition", "attachment; filename=\"logs.txt\"");
                await response.OutputStream.WriteAsync(content, 0, content.Length).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private async Task HandlePrintersApiAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            var absolutePath = request.Url.AbsolutePath.TrimStart('/');
            var path = absolutePath.ToLower();
            
            // 动态移除路径前缀（如 /printers/, /ipp/ 等）
            string remainingPath = null;
            var apiMatch = System.Text.RegularExpressions.Regex.Match(path, @"^[^/]+/api/printers/(.+)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            if (apiMatch.Success)
            {
                remainingPath = apiMatch.Groups[1].Value;
            }
            else if (path.StartsWith("api/printers/", StringComparison.OrdinalIgnoreCase))
            {
                remainingPath = absolutePath.Substring("api/printers/".Length);
            }
            else
            {
                // 检查是否是列表请求（没有剩余路径）
                var listMatch = System.Text.RegularExpressions.Regex.Match(path, @"^[^/]+/api/printers$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                if (listMatch.Success || path == "api/printers")
                {
                    remainingPath = "";
                }
            }
            
            if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                if (string.IsNullOrEmpty(remainingPath))
                {
                    // 获取所有打印机列表
                    await HandleGetPrintersListAsync(response, token).ConfigureAwait(false);
                    return;
                }
                
                // 获取特定打印机详情
                var printerName = Uri.UnescapeDataString(remainingPath);
                await HandleGetPrinterDetailsAsync(response, printerName, token).ConfigureAwait(false);
                return;
            }
            
            if (string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
            {
                // 添加共享队列
                var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                var bodyText = Encoding.UTF8.GetString(body);
                await HandleAddPrinterQueueAsync(response, bodyText, token).ConfigureAwait(false);
                return;
            }
            
            if (string.Equals(request.HttpMethod, "PUT", StringComparison.OrdinalIgnoreCase))
            {
                // 编辑共享队列（修改队列名或目标打印机）
                if (string.IsNullOrEmpty(remainingPath))
                {
                    var editError = new Dictionary<string, object> { ["error"] = "Printer name is required" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, editError).ConfigureAwait(false);
                    return;
                }
                var printerName = Uri.UnescapeDataString(remainingPath);
                var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                var bodyText = Encoding.UTF8.GetString(body);
                await HandleUpdatePrinterQueueAsync(response, printerName, bodyText, token).ConfigureAwait(false);
                return;
            }
            
            if (string.Equals(request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase))
            {
                // 移除共享队列
                if (string.IsNullOrEmpty(remainingPath))
                {
                    var deleteError = new Dictionary<string, object> { ["error"] = "Printer name is required" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, deleteError).ConfigureAwait(false);
                    return;
                }
                var printerName = Uri.UnescapeDataString(remainingPath);
                await HandleRemovePrinterQueueAsync(response, printerName, token).ConfigureAwait(false);
                return;
            }
            
            var methodError = new Dictionary<string, object> { ["error"] = "Method not allowed" };
            await WriteJsonResponse(response, HttpStatusCode.MethodNotAllowed, methodError).ConfigureAwait(false);
        }

        private async Task HandleGetPrintersListAsync(HttpListenerResponse response, CancellationToken token)
        {
            try
            {
                // 线程安全地获取当前配置
                ServerConfig currentConfig;
                lock (_configLock)
                {
                    currentConfig = _config;
                }
                
                var physicalPrinters = GetPhysicalPrinters();
                var sharedQueues = currentConfig.QueueToPrinter;
                var localIps = _getLocalIpAddresses();
                var port = _extractPortFromPrefix(currentConfig.BasePrefix);
                var baseUrl = localIps.Count > 0 ? $"http://{localIps[0]}:{port}" : currentConfig.BasePrefix.TrimEnd('/');
                
                var printers = new List<Dictionary<string, object>>();
                foreach (var printer in physicalPrinters)
                {
                    try
                    {
                        var isShared = sharedQueues.Any(kvp => kvp.Value == printer);
                        var queueName = isShared ? sharedQueues.First(kvp => kvp.Value == printer).Key : null;
                        var uri = queueName != null ? $"{baseUrl}/printers/{queueName}" : "";
                        
                        var printerInfo = new Dictionary<string, object>
                        {
                            ["name"] = printer,
                            ["isShared"] = isShared,
                            ["queueName"] = queueName ?? (object)null,
                            ["uri"] = uri,
                            ["status"] = GetPrinterStatus(printer)
                        };
                        printers.Add(printerInfo);
                    }
                    catch (Exception ex)
                    {
                        Log($"获取打印机信息失败 ({printer}): {ex.Message}");
                    }
                }
                
                var result = new Dictionary<string, object>
                {
                    ["printers"] = printers,
                    ["isServiceRunning"] = _listener.IsListening
                };
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"HandleGetPrintersListAsync 错误: {ex.Message}");
                var listError = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, listError).ConfigureAwait(false);
            }
        }

        private async Task HandleGetPrinterDetailsAsync(HttpListenerResponse response, string printerName, CancellationToken token)
        {
            var details = GetPrinterDetails(printerName);
            await WriteJsonResponse(response, HttpStatusCode.OK, details).ConfigureAwait(false);
        }

        private async Task HandleAddPrinterQueueAsync(HttpListenerResponse response, string bodyText, CancellationToken token)
        {
            try
            {
                var printerName = "";
                var queueName = "";
                
                if (bodyText.Contains("\"printerName\""))
                {
                    var nameMatch = System.Text.RegularExpressions.Regex.Match(bodyText, @"""printerName""\s*:\s*""([^""]+)""");
                    if (nameMatch.Success) printerName = nameMatch.Groups[1].Value;
                }
                
                if (bodyText.Contains("\"queueName\""))
                {
                    var queueMatch = System.Text.RegularExpressions.Regex.Match(bodyText, @"""queueName""\s*:\s*""([^""]+)""");
                    if (queueMatch.Success) queueName = queueMatch.Groups[1].Value;
                }
                
                if (string.IsNullOrEmpty(printerName) || string.IsNullOrEmpty(queueName))
                {
                    var error = new Dictionary<string, object> { ["error"] = "printerName and queueName are required" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 验证队列名格式
                queueName = queueName.Trim();
                if (string.IsNullOrWhiteSpace(queueName) || queueName.Contains("/") || queueName.Contains("\\"))
                {
                    var error = new Dictionary<string, object> { ["error"] = "队列名称格式无效，不能包含 / 或 \\" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 验证打印机是否存在
                var physicalPrinters = GetPhysicalPrinters();
                if (!physicalPrinters.Contains(printerName))
                {
                    var error = new Dictionary<string, object> { ["error"] = "指定的打印机不存在" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 加载当前配置
                var settings = AppSettingsStore.Load();
                if (settings.PrinterMappings == null)
                {
                    settings.PrinterMappings = new List<PrinterMapping>();
                }
                
                // 检查队列名是否已存在
                if (settings.PrinterMappings.Any(m => string.Equals(m.QueueName, queueName, StringComparison.OrdinalIgnoreCase)))
                {
                    var error = new Dictionary<string, object> { ["error"] = $"队列名称 '{queueName}' 已存在" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 检查打印机是否已被其他队列使用
                if (settings.PrinterMappings.Any(m => string.Equals(m.PrinterName, printerName, StringComparison.OrdinalIgnoreCase)))
                {
                    var error = new Dictionary<string, object> { ["error"] = $"打印机 '{printerName}' 已被其他队列使用" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 添加新的映射
                settings.PrinterMappings.Add(new PrinterMapping
                {
                    QueueName = queueName,
                    PrinterName = printerName
                });
                
                // 保存配置
                AppSettingsStore.Save(settings);
                Log($"通过WEB界面添加共享队列：{queueName} -> {printerName}");
                
                // 触发配置热更新
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object>
                {
                    ["success"] = true,
                    ["message"] = $"已成功添加共享队列：{queueName} -> {printerName}，配置已实时生效"
                };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"添加共享队列失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        private async Task HandleRemovePrinterQueueAsync(HttpListenerResponse response, string printerName, CancellationToken token)
        {
            try
            {
                // 加载当前配置
                var settings = AppSettingsStore.Load();
                if (settings.PrinterMappings == null)
                {
                    settings.PrinterMappings = new List<PrinterMapping>();
                }
                
                // 查找并移除映射
                var mappingToRemove = settings.PrinterMappings.FirstOrDefault(m => 
                    string.Equals(m.PrinterName, printerName, StringComparison.OrdinalIgnoreCase));
                
                if (mappingToRemove == null)
                {
                    var error = new Dictionary<string, object> { ["error"] = "打印机未在共享队列中" };
                    await WriteJsonResponse(response, HttpStatusCode.NotFound, error).ConfigureAwait(false);
                    return;
                }
                
                var queueName = mappingToRemove.QueueName;
                settings.PrinterMappings.Remove(mappingToRemove);
                
                // 保存配置
                AppSettingsStore.Save(settings);
                Log($"通过WEB界面移除共享队列：{queueName} -> {printerName}");
                
                // 触发配置热更新
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object>
                {
                    ["success"] = true,
                    ["message"] = $"已成功移除共享队列：{queueName} -> {printerName}，配置已实时生效"
                };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"移除共享队列失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleTcpApiAsync(HttpListenerRequest request, HttpListenerResponse response, CancellationToken token)
        {
            var absolutePath = request.Url.AbsolutePath.TrimStart('/');
            var path = absolutePath.ToLower();
            
            // 动态移除路径前缀
            string remainingPath = null;
            var apiMatch = System.Text.RegularExpressions.Regex.Match(path, @"^[^/]+/api/tcp/(.+)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            if (apiMatch.Success)
            {
                remainingPath = apiMatch.Groups[1].Value;
            }
            else if (path.StartsWith("api/tcp/", StringComparison.OrdinalIgnoreCase))
            {
                remainingPath = absolutePath.Substring("api/tcp/".Length);
            }
            else if (path == "api/tcp")
            {
                remainingPath = "";
            }
            
            if (string.IsNullOrEmpty(remainingPath) || remainingPath == "tasks")
            {
                if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleGetTcpTasksAsync(response).ConfigureAwait(false);
                    return;
                }
                if (string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                    var bodyText = Encoding.UTF8.GetString(body);
                    await HandleAddTcpTaskAsync(response, bodyText, token).ConfigureAwait(false);
                    return;
                }
            }
            
            if (remainingPath.StartsWith("tasks/"))
            {
                var taskIndexStr = remainingPath.Substring("tasks/".Length);
                if (int.TryParse(taskIndexStr, out var taskIndex))
                {
                    if (string.Equals(request.HttpMethod, "PUT", StringComparison.OrdinalIgnoreCase))
                    {
                        var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                        var bodyText = Encoding.UTF8.GetString(body);
                        await HandleUpdateTcpTaskAsync(response, taskIndex, bodyText, token).ConfigureAwait(false);
                        return;
                    }
                    if (string.Equals(request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase))
                    {
                        await HandleRemoveTcpTaskAsync(response, taskIndex, token).ConfigureAwait(false);
                        return;
                    }
                }
            }
            
            if (remainingPath == "jobs")
            {
                if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleGetTcpJobsAsync(response).ConfigureAwait(false);
                    return;
                }
            }
            
            var methodError = new Dictionary<string, object> { ["error"] = "Method not allowed or endpoint not found" };
            await WriteJsonResponse(response, HttpStatusCode.MethodNotAllowed, methodError).ConfigureAwait(false);
        }
        
        private async Task HandleGetTcpTasksAsync(HttpListenerResponse response)
        {
            try
            {
                var tasks = _getTcpTasksCallback?.Invoke() ?? new List<Configuration.TcpPrinterTask>();
                var result = tasks.Select(t => new Dictionary<string, object>
                {
                    ["printerName"] = t.PrinterName,
                    ["port"] = t.Port,
                    ["printMode"] = t.PrintMode
                }).ToList();
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"获取 TCP 任务列表失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleAddTcpTaskAsync(HttpListenerResponse response, string bodyText, CancellationToken token)
        {
            try
            {
                var data = ParseJsonToDictionary(bodyText);
                var printerName = data.ContainsKey("printerName") ? data["printerName"]?.ToString() : "";
                var port = data.ContainsKey("port") && int.TryParse(data["port"]?.ToString(), out var p) ? p : 9100;
                var printMode = data.ContainsKey("printMode") ? data["printMode"]?.ToString() : "自动";
                
                if (string.IsNullOrWhiteSpace(printerName))
                {
                    var error = new Dictionary<string, object> { ["error"] = "printerName is required" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                if (port < 1 || port > 65535)
                {
                    var error = new Dictionary<string, object> { ["error"] = "端口范围必须在 1 - 65535 之间" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                var task = new Configuration.TcpPrinterTask
                {
                    PrinterName = printerName.Trim(),
                    Port = port,
                    PrintMode = string.IsNullOrWhiteSpace(printMode) ? "自动" : printMode.Trim()
                };
                
                var result = _addTcpTaskCallback?.Invoke(task) ?? (false, "TCP 服务回调未设置");
                
                if (result.Success)
                {
                    Log($"通过WEB界面添加 TCP 任务：{task.PrinterName}@{task.Port} ({task.PrintMode})");
                }
                
                await WriteJsonResponse(response, result.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest, 
                    new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    }).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"添加 TCP 任务失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleUpdateTcpTaskAsync(HttpListenerResponse response, int index, string bodyText, CancellationToken token)
        {
            try
            {
                var data = ParseJsonToDictionary(bodyText);
                var printerName = data.ContainsKey("printerName") ? data["printerName"]?.ToString() : "";
                var port = data.ContainsKey("port") && int.TryParse(data["port"]?.ToString(), out var p) ? p : 9100;
                var printMode = data.ContainsKey("printMode") ? data["printMode"]?.ToString() : "自动";
                
                if (string.IsNullOrWhiteSpace(printerName))
                {
                    var error = new Dictionary<string, object> { ["error"] = "printerName is required" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                if (port < 1 || port > 65535)
                {
                    var error = new Dictionary<string, object> { ["error"] = "端口范围必须在 1 - 65535 之间" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                var task = new Configuration.TcpPrinterTask
                {
                    PrinterName = printerName.Trim(),
                    Port = port,
                    PrintMode = string.IsNullOrWhiteSpace(printMode) ? "自动" : printMode.Trim()
                };
                
                var result = _updateTcpTaskCallback?.Invoke(index, task) ?? (false, "TCP 服务回调未设置");
                
                if (result.Success)
                {
                    Log($"通过WEB界面更新 TCP 任务[{index}]：{task.PrinterName}@{task.Port} ({task.PrintMode})");
                }
                
                await WriteJsonResponse(response, result.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest, 
                    new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    }).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"更新 TCP 任务失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleRemoveTcpTaskAsync(HttpListenerResponse response, int index, CancellationToken token)
        {
            try
            {
                var result = _removeTcpTaskCallback?.Invoke(index) ?? (false, "TCP 服务回调未设置");
                
                if (result.Success)
                {
                    Log($"通过WEB界面删除 TCP 任务[{index}]");
                }
                
                await WriteJsonResponse(response, result.Success ? HttpStatusCode.OK : HttpStatusCode.BadRequest, 
                    new Dictionary<string, object>
                    {
                        ["success"] = result.Success,
                        ["message"] = result.Message
                    }).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"删除 TCP 任务失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleGetTcpJobsAsync(HttpListenerResponse response)
        {
            try
            {
                var jobs = _getTcpJobsCallback?.Invoke() ?? new List<IppServerHost.TcpJob>();
                var jobList = jobs.Select(j => new Dictionary<string, object>
                {
                    ["jobId"] = j.JobId,
                    ["port"] = j.Port,
                    ["printerName"] = j.PrinterName,
                    ["clientIp"] = j.ClientIp,
                    ["printMode"] = j.PrintMode,
                    ["dataSizeBytes"] = j.DataSizeBytes,
                    ["status"] = j.JobState == 9 ? "completed" : j.JobState == 8 ? "cancelled" : j.JobState == 5 ? "printing" : "pending",
                    ["errorMessage"] = j.ErrorMessage ?? "",
                    ["createdTime"] = j.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"),
                    ["completedTime"] = j.CompletedTime != default(DateTime) ? j.CompletedTime.ToString("yyyy-MM-dd HH:mm:ss") : ""
                }).ToList();
                
                await WriteJsonResponse(response, HttpStatusCode.OK, jobList).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"获取 TCP 作业列表失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleUpdatePrinterQueueAsync(HttpListenerResponse response, string printerName, string bodyText, CancellationToken token)
        {
            try
            {
                var newQueueName = "";
                var newPrinterName = "";
                
                if (bodyText.Contains("\"queueName\""))
                {
                    var queueMatch = System.Text.RegularExpressions.Regex.Match(bodyText, @"""queueName""\s*:\s*""([^""]+)""");
                    if (queueMatch.Success) newQueueName = queueMatch.Groups[1].Value.Trim();
                }
                
                if (bodyText.Contains("\"printerName\""))
                {
                    var nameMatch = System.Text.RegularExpressions.Regex.Match(bodyText, @"""printerName""\s*:\s*""([^""]+)""");
                    if (nameMatch.Success) newPrinterName = nameMatch.Groups[1].Value.Trim();
                }
                
                // 加载当前配置
                var settings = AppSettingsStore.Load();
                if (settings.PrinterMappings == null)
                {
                    settings.PrinterMappings = new List<PrinterMapping>();
                }
                
                // 查找要编辑的映射
                var mappingToUpdate = settings.PrinterMappings.FirstOrDefault(m => 
                    string.Equals(m.PrinterName, printerName, StringComparison.OrdinalIgnoreCase));
                
                if (mappingToUpdate == null)
                {
                    var error = new Dictionary<string, object> { ["error"] = "打印机未在共享队列中" };
                    await WriteJsonResponse(response, HttpStatusCode.NotFound, error).ConfigureAwait(false);
                    return;
                }
                
                var oldQueueName = mappingToUpdate.QueueName;
                var oldPrinterName = mappingToUpdate.PrinterName;
                
                // 更新队列名（如果提供）
                if (!string.IsNullOrEmpty(newQueueName))
                {
                    // 验证队列名格式
                    if (newQueueName.Contains("/") || newQueueName.Contains("\\"))
                    {
                        var error = new Dictionary<string, object> { ["error"] = "队列名称格式无效，不能包含 / 或 \\" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                        return;
                    }
                    
                    // 检查新队列名是否已被其他队列使用
                    if (settings.PrinterMappings.Any(m => 
                        !string.Equals(m.PrinterName, printerName, StringComparison.OrdinalIgnoreCase) &&
                        string.Equals(m.QueueName, newQueueName, StringComparison.OrdinalIgnoreCase)))
                    {
                        var error = new Dictionary<string, object> { ["error"] = $"队列名称 '{newQueueName}' 已被其他队列使用" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                        return;
                    }
                    
                    mappingToUpdate.QueueName = newQueueName;
                }
                
                // 更新打印机名（如果提供）
                if (!string.IsNullOrEmpty(newPrinterName))
                {
                    // 验证打印机是否存在
                    var physicalPrinters = GetPhysicalPrinters();
                    if (!physicalPrinters.Contains(newPrinterName))
                    {
                        var error = new Dictionary<string, object> { ["error"] = "指定的打印机不存在" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                        return;
                    }
                    
                    // 检查新打印机是否已被其他队列使用
                    if (settings.PrinterMappings.Any(m => 
                        !string.Equals(m.QueueName, mappingToUpdate.QueueName, StringComparison.OrdinalIgnoreCase) &&
                        string.Equals(m.PrinterName, newPrinterName, StringComparison.OrdinalIgnoreCase)))
                    {
                        var error = new Dictionary<string, object> { ["error"] = $"打印机 '{newPrinterName}' 已被其他队列使用" };
                        await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                        return;
                    }
                    
                    mappingToUpdate.PrinterName = newPrinterName;
                }
                
                if (string.IsNullOrEmpty(newQueueName) && string.IsNullOrEmpty(newPrinterName))
                {
                    var error = new Dictionary<string, object> { ["error"] = "至少需要提供 queueName 或 printerName 之一" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 保存配置
                AppSettingsStore.Save(settings);
                Log($"通过WEB界面更新共享队列：{oldQueueName} -> {oldPrinterName} 更新为 {mappingToUpdate.QueueName} -> {mappingToUpdate.PrinterName}");
                
                // 触发配置热更新
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object>
                {
                    ["success"] = true,
                    ["message"] = $"已成功更新共享队列：{mappingToUpdate.QueueName} -> {mappingToUpdate.PrinterName}，配置已实时生效"
                };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Log($"更新共享队列失败：{ex.Message}");
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }

        #endregion

        #region 辅助方法

        private static List<string> GetPhysicalPrinters()
        {
            // 使用公共的打印机过滤工具类，与 GUI 端保持一致的过滤逻辑
            // 该工具类使用 WMI + 端口/驱动名称综合判断，比仅通过名称关键字更准确
            return Printing.PrinterFilter.GetPhysicalPrinters();
        }

        private static Dictionary<string, object> GetPrinterDetails(string printerName)
        {
            var details = new Dictionary<string, object>();
            try
            {
                var settings = new PrinterSettings { PrinterName = printerName };
                details["name"] = printerName;
                details["isValid"] = settings.IsValid;
                details["isDefault"] = settings.IsDefaultPrinter;
                details["canDuplex"] = settings.CanDuplex;
                details["supportsColor"] = settings.SupportsColor;
                details["maximumCopies"] = settings.MaximumCopies;
                
                try
                {
                    var status = GetPrinterStatus(printerName);
                    details["status"] = status;
                }
                catch
                {
                    details["status"] = "未知";
                }
                
                var paperSizes = new List<Dictionary<string, object>>();
                foreach (PaperSize ps in settings.PaperSizes)
                {
                    paperSizes.Add(new Dictionary<string, object>
                    {
                        ["name"] = ps.PaperName,
                        ["width"] = ps.Width,
                        ["height"] = ps.Height
                    });
                }
                details["paperSizes"] = paperSizes;
            }
            catch
            {
                details["name"] = printerName;
            }
            return details;
        }

        private static string GetPrinterStatus(string printerName)
        {
            try
            {
                var settings = new PrinterSettings { PrinterName = printerName };
                if (!settings.IsValid)
                {
                    return "离线";
                }
                return "在线";
            }
            catch
            {
                return "未知";
            }
        }

        private static string MapJobStatus(int jobState)
        {
            return jobState switch
            {
                5 => "printing",
                9 => "completed",
                7 => "stopped",
                6 => "error",
                8 => "error",
                _ => "pending"
            };
        }

        private static string GetJobStateText(int jobState)
        {
            return jobState switch
            {
                3 => "等待中",
                4 => "挂起",
                5 => "打印中",
                6 => "错误",
                7 => "已停止",
                8 => "错误",
                9 => "已完成",
                _ => "未知"
            };
        }

        private static string FormatFileSize(long bytes)
        {
            if (bytes <= 0)
            {
                return "0 B";
            }
            string[] units = { "B", "KB", "MB", "GB", "TB" };
            double size = bytes;
            var order = 0;
            while (size >= 1024 && order < units.Length - 1)
            {
                order++;
                size /= 1024;
            }
            return $"{size:0.##} {units[order]}";
        }

        private static bool CanStopJob(IppServerHost.CompletedJob job)
        {
            if (job == null)
            {
                return false;
            }
            return job.JobState == 3 || job.JobState == 4 || job.JobState == 5;
        }

        private static bool CanReplayJob(IppServerHost.CompletedJob job)
        {
            if (job == null)
            {
                return false;
            }
            if (job.JobState != 9 || !job.CanReplay)
            {
                return false;
            }
            if (string.IsNullOrWhiteSpace(job.PayloadPath))
            {
                return false;
            }
            return File.Exists(job.PayloadPath);
        }

        private static void CleanupCompletedJobs(List<IppServerHost.CompletedJob> jobList, DateTime now)
        {
            // Web 管理界面不再自动删除已完成/错误作业，保留记录以便日后查询。
            // 该方法保留以兼容旧调用，目前仅在队列记录达到上限时通过宿主进行清理。
        }

        private static async Task<byte[]> ReadBodyAsync(Stream input)
        {
            using (var ms = new MemoryStream())
            {
                await input.CopyToAsync(ms).ConfigureAwait(false);
                return ms.ToArray();
            }
        }

        private async Task WriteJsonResponse(HttpListenerResponse response, HttpStatusCode statusCode, object data)
        {
            string json;
            
            if (data is Dictionary<string, object> dict)
            {
                json = SerializeDictionaryToJson(dict);
            }
            else
            {
                try
                {
                    using (var stream = new MemoryStream())
                    {
                        var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(data.GetType(), new System.Runtime.Serialization.Json.DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true });
                        serializer.WriteObject(stream, data);
                        json = Encoding.UTF8.GetString(stream.ToArray());
                    }
                }
                catch
                {
                    json = "{}";
                }
            }
            
            var buffer = Encoding.UTF8.GetBytes(json);
            response.StatusCode = (int)statusCode;
            response.ContentType = "application/json; charset=utf-8";
            response.ContentLength64 = buffer.Length;
            await response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
        }

        private static string SerializeDictionaryToJson(Dictionary<string, object> dict)
        {
            var sb = new StringBuilder();
            sb.Append("{");
            var first = true;
            foreach (var kvp in dict)
            {
                if (!first) sb.Append(",");
                first = false;
                sb.Append("\"");
                sb.Append(EscapeJsonString(kvp.Key));
                sb.Append("\":");
                sb.Append(SerializeValueToJson(kvp.Value));
            }
            sb.Append("}");
            return sb.ToString();
        }

        private static string SerializeValueToJson(object value)
        {
            if (value == null)
            {
                return "null";
            }
            
            if (value is string str)
            {
                return "\"" + EscapeJsonString(str) + "\"";
            }
            
            if (value is bool b)
            {
                return b ? "true" : "false";
            }
            
            if (value is int || value is long || value is short || value is byte)
            {
                return value.ToString();
            }
            
            if (value is double || value is float || value is decimal)
            {
                return value.ToString().Replace(',', '.');
            }
            
            if (value is Dictionary<string, object> dict)
            {
                return SerializeDictionaryToJson(dict);
            }
            
            if (value is System.Collections.IEnumerable enumerable && !(value is string))
            {
                var sb = new StringBuilder();
                sb.Append("[");
                var first = true;
                foreach (var item in enumerable)
                {
                    if (!first) sb.Append(",");
                    first = false;
                    sb.Append(SerializeValueToJson(item));
                }
                sb.Append("]");
                return sb.ToString();
            }
            
            return "\"" + EscapeJsonString(value.ToString()) + "\"";
        }

        private static string EscapeJsonString(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return "";
            }
            
            var sb = new StringBuilder();
            foreach (var c in str)
            {
                switch (c)
                {
                    case '"':
                        sb.Append("\\\"");
                        break;
                    case '\\':
                        sb.Append("\\\\");
                        break;
                    case '\b':
                        sb.Append("\\b");
                        break;
                    case '\f':
                        sb.Append("\\f");
                        break;
                    case '\n':
                        sb.Append("\\n");
                        break;
                    case '\r':
                        sb.Append("\\r");
                        break;
                    case '\t':
                        sb.Append("\\t");
                        break;
                    default:
                        if (c < 0x20)
                        {
                            sb.AppendFormat("\\u{0:X4}", (int)c);
                        }
                        else
                        {
                            sb.Append(c);
                        }
                        break;
                }
            }
            return sb.ToString();
        }

        private async Task HandleRestrictionApiAsync(HttpListenerRequest request, HttpListenerResponse response, string remainingPath, CancellationToken token)
        {
            try
            {
                var pathParts = remainingPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                
                // GET /api/system/restriction - 获取限制配置
                if (pathParts.Length == 1 && string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    await HandleGetRestrictionAsync(response).ConfigureAwait(false);
                    return;
                }
                
                // PUT /api/system/restriction - 更新限制配置（启用/禁用、模式）
                if (pathParts.Length == 1 && string.Equals(request.HttpMethod, "PUT", StringComparison.OrdinalIgnoreCase))
                {
                    var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                    var bodyText = Encoding.UTF8.GetString(body);
                    await HandleUpdateRestrictionAsync(response, bodyText).ConfigureAwait(false);
                    return;
                }
                
                // POST /api/system/restriction/ip - 添加IP规则
                if (pathParts.Length == 2 && pathParts[1] == "ip" && string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                    var bodyText = Encoding.UTF8.GetString(body);
                    await HandleAddIpRuleAsync(response, bodyText).ConfigureAwait(false);
                    return;
                }
                
                // DELETE /api/system/restriction/ip/{index} - 删除IP规则
                if (pathParts.Length == 3 && pathParts[1] == "ip" && string.Equals(request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase))
                {
                    if (int.TryParse(pathParts[2], out var index))
                    {
                        await HandleRemoveIpRuleAsync(response, index).ConfigureAwait(false);
                        return;
                    }
                }
                
                // POST /api/system/restriction/mac - 添加MAC规则
                if (pathParts.Length == 2 && pathParts[1] == "mac" && string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    var body = await ReadBodyAsync(request.InputStream).ConfigureAwait(false);
                    var bodyText = Encoding.UTF8.GetString(body);
                    await HandleAddMacRuleAsync(response, bodyText).ConfigureAwait(false);
                    return;
                }
                
                // DELETE /api/system/restriction/mac/{index} - 删除MAC规则
                if (pathParts.Length == 3 && pathParts[1] == "mac" && string.Equals(request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase))
                {
                    if (int.TryParse(pathParts[2], out var index))
                    {
                        await HandleRemoveMacRuleAsync(response, index).ConfigureAwait(false);
                        return;
                    }
                }
                
                var error = new Dictionary<string, object> { ["error"] = "API endpoint not found" };
                await WriteJsonResponse(response, HttpStatusCode.NotFound, error).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleGetRestrictionAsync(HttpListenerResponse response)
        {
            try
            {
                var settings = Configuration.AppSettingsStore.Load();
                var restriction = settings.IpMacRestriction ?? Configuration.IpMacRestriction.CreateDefault();
                
                var result = new Dictionary<string, object>
                {
                    ["enabled"] = restriction.Enabled,
                    ["whitelistMode"] = restriction.WhitelistMode,
                    ["ipRules"] = restriction.IpRules ?? new List<string>(),
                    ["macRules"] = restriction.MacRules ?? new List<string>()
                };
                
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["error"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.InternalServerError, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleUpdateRestrictionAsync(HttpListenerResponse response, string bodyText)
        {
            try
            {
                var data = ParseJsonToDictionary(bodyText);
                var settings = Configuration.AppSettingsStore.Load();
                
                if (settings.IpMacRestriction == null)
                {
                    settings.IpMacRestriction = Configuration.IpMacRestriction.CreateDefault();
                }
                
                if (data.ContainsKey("enabled"))
                {
                    settings.IpMacRestriction.Enabled = Convert.ToBoolean(data["enabled"]);
                }
                
                if (data.ContainsKey("whitelistMode"))
                {
                    settings.IpMacRestriction.WhitelistMode = Convert.ToBoolean(data["whitelistMode"]);
                }
                
                Configuration.AppSettingsStore.Save(settings);
                
                // 通知服务重载配置（GUI端会通过FileSystemWatcher自动检测并刷新）
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object> { ["success"] = true };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleAddIpRuleAsync(HttpListenerResponse response, string bodyText)
        {
            try
            {
                var data = ParseJsonToDictionary(bodyText);
                var rule = data.ContainsKey("rule") ? data["rule"]?.ToString() : "";
                
                if (string.IsNullOrWhiteSpace(rule))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "IP规则不能为空" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                if (!Configuration.IpMacValidator.ValidateIpRule(rule, out var errorMessage))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = $"IP规则格式无效：{errorMessage}" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                var settings = Configuration.AppSettingsStore.Load();
                if (settings.IpMacRestriction == null)
                {
                    settings.IpMacRestriction = Configuration.IpMacRestriction.CreateDefault();
                }
                
                if (settings.IpMacRestriction.IpRules == null)
                {
                    settings.IpMacRestriction.IpRules = new List<string>();
                }
                
                if (settings.IpMacRestriction.IpRules.Contains(rule))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "该IP规则已存在" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                settings.IpMacRestriction.IpRules.Add(rule);
                Configuration.AppSettingsStore.Save(settings);
                
                // 通知服务重载配置（GUI端会通过FileSystemWatcher自动检测并刷新）
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object> { ["success"] = true };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleRemoveIpRuleAsync(HttpListenerResponse response, int index)
        {
            try
            {
                var settings = Configuration.AppSettingsStore.Load();
                if (settings.IpMacRestriction == null || settings.IpMacRestriction.IpRules == null || index < 0 || index >= settings.IpMacRestriction.IpRules.Count)
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "无效的索引" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                settings.IpMacRestriction.IpRules.RemoveAt(index);
                Configuration.AppSettingsStore.Save(settings);
                
                // 通知服务重载配置（GUI端会通过FileSystemWatcher自动检测并刷新）
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object> { ["success"] = true };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleAddMacRuleAsync(HttpListenerResponse response, string bodyText)
        {
            try
            {
                var data = ParseJsonToDictionary(bodyText);
                var rule = data.ContainsKey("rule") ? data["rule"]?.ToString() : "";
                
                if (string.IsNullOrWhiteSpace(rule))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "MAC规则不能为空" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                if (!Configuration.IpMacValidator.ValidateMacRule(rule, out var errorMessage))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = $"MAC规则格式无效：{errorMessage}" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                // 标准化MAC地址格式（支持多种输入格式）
                rule = Configuration.IpMacValidator.NormalizeMacAddress(rule);
                if (rule == null)
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "MAC规则格式无效，无法标准化" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                var settings = Configuration.AppSettingsStore.Load();
                if (settings.IpMacRestriction == null)
                {
                    settings.IpMacRestriction = Configuration.IpMacRestriction.CreateDefault();
                }
                
                if (settings.IpMacRestriction.MacRules == null)
                {
                    settings.IpMacRestriction.MacRules = new List<string>();
                }
                
                if (settings.IpMacRestriction.MacRules.Contains(rule))
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "该MAC规则已存在" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                settings.IpMacRestriction.MacRules.Add(rule);
                Configuration.AppSettingsStore.Save(settings);
                
                // 通知服务重载配置（GUI端会通过FileSystemWatcher自动检测并刷新）
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object> { ["success"] = true };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
            }
        }
        
        private async Task HandleRemoveMacRuleAsync(HttpListenerResponse response, int index)
        {
            try
            {
                var settings = Configuration.AppSettingsStore.Load();
                if (settings.IpMacRestriction == null || settings.IpMacRestriction.MacRules == null || index < 0 || index >= settings.IpMacRestriction.MacRules.Count)
                {
                    var error = new Dictionary<string, object> { ["success"] = false, ["message"] = "无效的索引" };
                    await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
                    return;
                }
                
                settings.IpMacRestriction.MacRules.RemoveAt(index);
                Configuration.AppSettingsStore.Save(settings);
                
                // 通知服务重载配置（GUI端会通过FileSystemWatcher自动检测并刷新）
                _reloadConfigCallback?.Invoke();
                
                var result = new Dictionary<string, object> { ["success"] = true };
                await WriteJsonResponse(response, HttpStatusCode.OK, result).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var error = new Dictionary<string, object> { ["success"] = false, ["message"] = ex.Message };
                await WriteJsonResponse(response, HttpStatusCode.BadRequest, error).ConfigureAwait(false);
            }
        }

        private string LoadRestrictionHtml()
        {
            try
            {
                var assembly = System.Reflection.Assembly.GetExecutingAssembly();
                var resourceName = assembly.GetManifestResourceNames()
                    .FirstOrDefault(n => n.EndsWith("restriction.html", StringComparison.OrdinalIgnoreCase));
                
                if (resourceName != null)
                {
                    using (var stream = assembly.GetManifestResourceStream(resourceName))
                    using (var reader = new StreamReader(stream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                Log($"从程序集资源加载 restriction.html 失败：{ex.Message}");
            }
            // 如果加载失败，返回默认内容
            return @"<div id=""tab-restriction"" class=""tab-pane""><div class=""loading""><div class=""spinner""></div><p>正在加载IP/MAC限制配置...</p></div></div>";
        }
        
        private string LoadRestrictionJs()
        {
            try
            {
                var assembly = System.Reflection.Assembly.GetExecutingAssembly();
                var resourceName = assembly.GetManifestResourceNames()
                    .FirstOrDefault(n => n.EndsWith("restriction.js", StringComparison.OrdinalIgnoreCase));
                
                if (resourceName != null)
                {
                    using (var stream = assembly.GetManifestResourceStream(resourceName))
                    using (var reader = new StreamReader(stream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                Log($"从程序集资源加载 restriction.js 失败：{ex.Message}");
            }
            // 如果加载失败，返回内联的JavaScript代码作为后备
            return GetInlineRestrictionJs();
        }
        
        private string GetInlineRestrictionJs()
        {
            // 内联JavaScript代码，作为文件加载失败时的后备方案
            return @"
// IP/MAC限制管理JavaScript代码（内联版本）
var selectedIpRuleIndex = -1;
var selectedMacRuleIndex = -1;

function loadRestriction() {
    var container = document.getElementById('tab-restriction');
    if (!container) {
        console.error('找不到 tab-restriction 容器');
        return;
    }
    container.style.display = 'block';
    var ipRulesList = document.getElementById('ipRulesList');
    var macRulesList = document.getElementById('macRulesList');
    if (!ipRulesList || !macRulesList) {
        console.error('找不到规则列表容器');
        setTimeout(function() { loadRestriction(); }, 100);
        return;
    }
    if (ipRulesList) {
        ipRulesList.innerHTML = '<div class=""loading""><div class=""spinner""></div><p>正在加载...</p></div>';
    }
    if (macRulesList) {
        macRulesList.innerHTML = '<div class=""loading""><div class=""spinner""></div><p>正在加载...</p></div>';
    }
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    console.log('调用API:', apiBase + '/restriction');
    fetch(apiBase + '/restriction')
        .then(function(response) {
            console.log('API响应状态:', response.status);
            if (!response.ok) {
                return response.text().then(function(text) {
                    console.error('API错误响应:', text);
                    throw new Error('HTTP ' + response.status + ': ' + text);
                });
            }
            return response.json();
        })
        .then(function(data) {
            console.log('API返回数据:', data);
            renderRestriction(data);
        })
        .catch(function(error) {
            console.error('加载IP/MAC限制配置失败：', error);
            var errorText = (error.message || '未知错误').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/""/g, '&quot;').replace(/'/g, '&#39;');
            var errorMsg = '<div class=""error"" style=""padding: 20px; color: #dc3545; text-align: center;"">加载IP/MAC限制配置失败：' + errorText + '</div>';
            if (ipRulesList) ipRulesList.innerHTML = errorMsg;
            if (macRulesList) macRulesList.innerHTML = errorMsg;
        });
}

function renderRestriction(data) {
    try {
        var enabled = data.enabled || false;
        var whitelistMode = data.whitelistMode || false;
        var ipRules = data.ipRules || [];
        var macRules = data.macRules || [];
        var chkEnable = document.getElementById('chkEnableRestriction');
        if (chkEnable) chkEnable.checked = enabled;
        var blacklistRadio = document.querySelector('#tab-restriction input[name=""restrictionMode""][value=""blacklist""]');
        var whitelistRadio = document.querySelector('#tab-restriction input[name=""restrictionMode""][value=""whitelist""]');
        if (blacklistRadio) blacklistRadio.checked = !whitelistMode;
        if (whitelistRadio) whitelistRadio.checked = whitelistMode;
        updateRestrictionControlsState(enabled);
        renderIpRulesList(ipRules);
        renderMacRulesList(macRules);
    } catch (error) {
        console.error('渲染限制配置失败：', error);
        var container = document.getElementById('tab-restriction');
        if (container) {
            var errorText = (error.message || '未知错误').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/""/g, '&quot;').replace(/'/g, '&#39;');
            container.innerHTML = '<div class=""error"" style=""padding: 20px; color: #dc3545;"">渲染配置失败：' + errorText + '</div>';
        }
    }
}

function updateRestrictionControlsState(enabled) {
    var container = document.getElementById('tab-restriction');
    if (!container) return;
    var controls = container.querySelectorAll('input[type=""radio""], button, table');
    controls.forEach(function(ctrl) { ctrl.disabled = !enabled; });
    var ipRulesList = document.getElementById('ipRulesList');
    var macRulesList = document.getElementById('macRulesList');
    if (ipRulesList) ipRulesList.style.opacity = enabled ? '1' : '0.5';
    if (macRulesList) macRulesList.style.opacity = enabled ? '1' : '0.5';
}

function renderIpRulesList(ipRules) {
    var container = document.getElementById('ipRulesList');
    if (!container) { console.error('找不到 ipRulesList 容器'); return; }
    if (ipRules.length === 0) {
        container.innerHTML = '<p style=""color: #666; text-align: center; padding: 20px;"">暂无IP规则</p>';
        return;
    }
    var html = '<table class=""queue-table"" style=""width: 100%;"">';
    html += '<thead><tr><th style=""width: 60px;"">序号</th><th>IP规则</th></tr></thead>';
    html += '<tbody>';
    for (var i = 0; i < ipRules.length; i++) {
        var rule = ipRules[i] || '';
        var escapedRule = rule.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/""/g, '&quot;').replace(/'/g, '&#39;');
        html += '<tr onclick=""selectIpRuleRow(this)"" style=""cursor: pointer;"">';
        html += '<td>' + (i + 1) + '</td>';
        html += '<td>' + escapedRule + '</td>';
        html += '</tr>';
    }
    html += '</tbody></table>';
    container.innerHTML = html;
    selectedIpRuleIndex = -1;
}

function renderMacRulesList(macRules) {
    var container = document.getElementById('macRulesList');
    if (!container) { console.error('找不到 macRulesList 容器'); return; }
    if (macRules.length === 0) {
        container.innerHTML = '<p style=""color: #666; text-align: center; padding: 20px;"">暂无MAC规则</p>';
        return;
    }
    var html = '<table class=""queue-table"" style=""width: 100%;"">';
    html += '<thead><tr><th style=""width: 60px;"">序号</th><th>MAC规则</th></tr></thead>';
    html += '<tbody>';
    for (var i = 0; i < macRules.length; i++) {
        var rule = macRules[i] || '';
        var escapedRule = rule.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/""/g, '&quot;').replace(/'/g, '&#39;');
        html += '<tr onclick=""selectMacRuleRow(this)"" style=""cursor: pointer;"">';
        html += '<td>' + (i + 1) + '</td>';
        html += '<td>' + escapedRule + '</td>';
        html += '</tr>';
    }
    html += '</tbody></table>';
    container.innerHTML = html;
    selectedMacRuleIndex = -1;
}

function selectIpRuleRow(row) {
    document.querySelectorAll('#ipRulesList tbody tr').forEach(function(r) { r.style.backgroundColor = ''; });
    row.style.backgroundColor = '#e3f2fd';
    selectedIpRuleIndex = Array.from(row.parentNode.children).indexOf(row);
}

function selectMacRuleRow(row) {
    document.querySelectorAll('#macRulesList tbody tr').forEach(function(r) { r.style.backgroundColor = ''; });
    row.style.backgroundColor = '#e3f2fd';
    selectedMacRuleIndex = Array.from(row.parentNode.children).indexOf(row);
}

function updateRestrictionEnabled(enabled) {
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    fetch(apiBase + '/restriction', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ enabled: enabled })
    })
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            updateRestrictionControlsState(enabled);
        } else {
            alert('更新失败：' + (result.message || '未知错误'));
            var chk = document.getElementById('chkEnableRestriction');
            if (chk) chk.checked = !enabled;
        }
    })
    .catch(function(error) {
        alert('更新失败：' + error.message);
        var chk = document.getElementById('chkEnableRestriction');
        if (chk) chk.checked = !enabled;
    });
}

function updateRestrictionMode(whitelistMode) {
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    fetch(apiBase + '/restriction', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ whitelistMode: whitelistMode })
    })
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return response.json();
    })
    .then(function(result) {
        if (!result.success) {
            alert('更新失败：' + (result.message || '未知错误'));
            loadRestriction();
        }
    })
    .catch(function(error) {
        alert('更新失败：' + error.message);
        loadRestriction();
    });
}

function showAddIpRuleDialog() {
    var rule = prompt('请输入IP规则（支持单个IP、CIDR网段如192.168.1.0/24、IP范围如192.168.1.1-192.168.1.100）：');
    if (!rule || !rule.trim()) return;
    addIpRule(rule.trim());
}

function addIpRule(rule) {
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    fetch(apiBase + '/restriction/ip', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ rule: rule })
    })
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            loadRestriction();
        } else {
            alert('添加失败：' + (result.message || '未知错误'));
        }
    })
    .catch(function(error) {
        alert('添加失败：' + error.message);
    });
}

function removeSelectedIpRule() {
    if (selectedIpRuleIndex < 0) {
        alert('请先选择要删除的IP规则');
        return;
    }
    if (!confirm('确定要删除选中的IP规则吗？')) return;
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    fetch(apiBase + '/restriction/ip/' + selectedIpRuleIndex, {
        method: 'DELETE'
    })
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            selectedIpRuleIndex = -1;
            loadRestriction();
        } else {
            alert('删除失败：' + (result.message || '未知错误'));
        }
    })
    .catch(function(error) {
        alert('删除失败：' + error.message);
    });
}

function showAddMacRuleDialog() {
    var rule = prompt('请输入MAC规则（支持完整MAC地址如00:11:22:33:44:55或通配符如00:11:22:*:*:*）：', '00:11:22:33:44:55');
    if (!rule || !rule.trim()) return;
    addMacRule(rule.trim());
}

function addMacRule(rule) {
    rule = rule.toUpperCase().replace(/-/g, ':');
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    fetch(apiBase + '/restriction/mac', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ rule: rule })
    })
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            loadRestriction();
        } else {
            alert('添加失败：' + (result.message || '未知错误'));
        }
    })
    .catch(function(error) {
        alert('添加失败：' + error.message);
    });
}

function removeSelectedMacRule() {
    if (selectedMacRuleIndex < 0) {
        alert('请先选择要删除的MAC规则');
        return;
    }
    if (!confirm('确定要删除选中的MAC规则吗？')) return;
    var apiBase = typeof getSystemApiBase === 'function' ? getSystemApiBase() : '/api/system';
    fetch(apiBase + '/restriction/mac/' + selectedMacRuleIndex, {
        method: 'DELETE'
    })
    .then(function(response) {
        if (!response.ok) throw new Error('HTTP ' + response.status);
        return response.json();
    })
    .then(function(result) {
        if (result.success) {
            selectedMacRuleIndex = -1;
            loadRestriction();
        } else {
            alert('删除失败：' + (result.message || '未知错误'));
        }
    })
    .catch(function(error) {
        alert('删除失败：' + error.message);
    });
}
";
        }

        private void Log(string message)
        {
            _logCallback?.Invoke(message);
        }

        #endregion
    }
}

